[c++2a] Add semantic support for private module fragments.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 18 Apr 2019 21:12:54 +0000 (21:12 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 18 Apr 2019 21:12:54 +0000 (21:12 +0000)
llvm-svn: 358713

13 files changed:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/Module.h
clang/include/clang/Lex/ModuleMap.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/Decl.cpp
clang/lib/Lex/ModuleMap.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaModule.cpp
clang/test/CXX/basic/basic.link/p1.cpp
clang/test/CXX/basic/basic.link/p2.cpp [new file with mode: 0644]
clang/test/CXX/module/module.interface/p1.cpp [new file with mode: 0644]
clang/test/CXX/modules-ts/dcl.dcl/dcl.module/dcl.module.interface/p1.cpp

index 960cf6c..f8aef6f 100644 (file)
@@ -9276,8 +9276,20 @@ def note_global_module_introducer_missing : Note<
 def err_export_within_export : Error<
   "export declaration appears within another export declaration">;
 def err_export_not_in_module_interface : Error<
-  "export declaration can only be used within a module interface unit after "
-  "the module declaration">;
+  "export declaration can only be used within a module interface unit"
+  "%select{ after the module declaration|}0">;
+def err_export_in_private_module_fragment : Error<
+  "export declaration cannot be used in a private module fragment">;
+def note_private_module_fragment : Note<
+  "private module fragment begins here">;
+def err_private_module_fragment_not_module : Error<
+  "private module fragment declaration with no preceding module declaration">;
+def err_private_module_fragment_redefined : Error<
+  "private module fragment redefined">;
+def err_private_module_fragment_not_module_interface : Error<
+  "private module fragment in module implementation unit">;
+def note_not_module_interface_add_export : Note<
+  "add 'export' here if this is intended to be a module interface unit">;
 
 def ext_equivalent_internal_linkage_decl_in_modules : ExtWarn<
   "ambiguous use of internal linkage declaration %0 defined in multiple modules">,
index 0e891af..d632fe0 100644 (file)
@@ -77,9 +77,11 @@ public:
     /// This is a C++ Modules TS module interface unit.
     ModuleInterfaceUnit,
 
-    /// This is a fragment of the global module within some C++ Modules
-    /// TS module.
+    /// This is a fragment of the global module within some C++ module.
     GlobalModuleFragment,
+
+    /// This is the private module fragment within some C++ module.
+    PrivateModuleFragment,
   };
 
   /// The kind of this module.
@@ -111,6 +113,11 @@ public:
   /// eventually be exposed, for use in "private" modules.
   std::string ExportAsModule;
 
+  /// Does this Module scope describe part of the purview of a named C++ module?
+  bool isModulePurview() const {
+    return Kind == ModuleInterfaceUnit || Kind == PrivateModuleFragment;
+  }
+
 private:
   /// The submodules of this module, indexed by name.
   std::vector<Module *> SubModules;
index af236f5..36e97a1 100644 (file)
@@ -520,14 +520,18 @@ public:
                                                bool IsFramework,
                                                bool IsExplicit);
 
-  /// Create a 'global module' for a C++ Modules TS module interface unit.
+  /// Create a global module fragment for a C++ module unit.
   ///
-  /// We model the global module as a submodule of the module interface unit.
-  /// Unfortunately, we can't create the module interface unit's Module until
-  /// later, because we don't know what it will be called.
-  Module *createGlobalModuleForInterfaceUnit(SourceLocation Loc);
+  /// We model the global module fragment as a submodule of the module
+  /// interface unit. Unfortunately, we can't create the module interface
+  /// unit's Module until later, because we don't know what it will be called.
+  Module *createGlobalModuleFragmentForModuleUnit(SourceLocation Loc);
 
-  /// Create a new module for a C++ Modules TS module interface unit.
+  /// Create a global module fragment for a C++ module interface unit.
+  Module *createPrivateModuleFragmentForInterfaceUnit(Module *Parent,
+                                                      SourceLocation Loc);
+
+  /// Create a new module for a C++ module interface unit.
   /// The module must not already exist, and will be configured for the current
   /// compilation.
   ///
index 8582872..a2f4311 100644 (file)
@@ -1386,8 +1386,21 @@ public:
 
   void emitAndClearUnusedLocalTypedefWarnings();
 
+  enum TUFragmentKind {
+    /// The global module fragment, between 'module;' and a module-declaration.
+    Global,
+    /// A normal translation unit fragment. For a non-module unit, this is the
+    /// entire translation unit. Otherwise, it runs from the module-declaration
+    /// to the private-module-fragment (if any) or the end of the TU (if not).
+    Normal,
+    /// The private module fragment, between 'module :private;' and the end of
+    /// the translation unit.
+    Private
+  };
+
   void ActOnStartOfTranslationUnit();
   void ActOnEndOfTranslationUnit();
+  void ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind);
 
   void CheckDelegatingCtorCycles();
 
@@ -2180,10 +2193,7 @@ public:
   /// \param ModuleLoc The location of the 'module' keyword.
   /// \param PrivateLoc The location of the 'private' keyword.
   DeclGroupPtrTy ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc,
-                                                SourceLocation PrivateLoc) {
-    // FIXME
-    return DeclGroupPtrTy();
-  }
+                                                SourceLocation PrivateLoc);
 
   /// The parser has processed a module import declaration.
   ///
index 00014d0..9d97868 100644 (file)
@@ -567,6 +567,13 @@ static bool isSingleLineLanguageLinkage(const Decl &D) {
   return false;
 }
 
+/// Determine whether D is declared in the purview of a named module.
+static bool isInModulePurview(const NamedDecl *D) {
+  if (auto *M = D->getOwningModule())
+    return M->isModulePurview();
+  return false;
+}
+
 static bool isExportedFromModuleIntefaceUnit(const NamedDecl *D) {
   // FIXME: Handle isModulePrivate.
   switch (D->getModuleOwnershipKind()) {
@@ -575,8 +582,7 @@ static bool isExportedFromModuleIntefaceUnit(const NamedDecl *D) {
     return false;
   case Decl::ModuleOwnershipKind::Visible:
   case Decl::ModuleOwnershipKind::VisibleWhenImported:
-    if (auto *M = D->getOwningModule())
-      return M->Kind == Module::ModuleInterfaceUnit;
+    return isInModulePurview(D);
   }
   llvm_unreachable("unexpected module ownership kind");
 }
@@ -586,9 +592,8 @@ static LinkageInfo getInternalLinkageFor(const NamedDecl *D) {
   // as "module-internal linkage", which means that they have internal linkage
   // formally but can be indirectly accessed from outside the module via inline
   // functions and templates defined within the module.
-  if (auto *M = D->getOwningModule())
-    if (M->Kind == Module::ModuleInterfaceUnit)
-      return LinkageInfo(ModuleInternalLinkage, DefaultVisibility, false);
+  if (isInModulePurview(D))
+    return LinkageInfo(ModuleInternalLinkage, DefaultVisibility, false);
 
   return LinkageInfo::internal();
 }
@@ -598,11 +603,9 @@ static LinkageInfo getExternalLinkageFor(const NamedDecl *D) {
   //   - A name declared at namespace scope that does not have internal linkage
   //     by the previous rules and that is introduced by a non-exported
   //     declaration has module linkage.
-  if (auto *M = D->getOwningModule())
-    if (M->Kind == Module::ModuleInterfaceUnit)
-      if (!isExportedFromModuleIntefaceUnit(
-              cast<NamedDecl>(D->getCanonicalDecl())))
-        return LinkageInfo(ModuleLinkage, DefaultVisibility, false);
+  if (isInModulePurview(D) &&
+      !isExportedFromModuleIntefaceUnit(cast<NamedDecl>(D->getCanonicalDecl())))
+    return LinkageInfo(ModuleLinkage, DefaultVisibility, false);
 
   return LinkageInfo::external();
 }
@@ -1507,6 +1510,11 @@ Module *Decl::getOwningModuleForLinkage(bool IgnoreLinkage) const {
     }
     return InternalLinkage ? M->Parent : nullptr;
   }
+
+  case Module::PrivateModuleFragment:
+    // The private module fragment is part of its containing module for linkage
+    // purposes.
+    return M->Parent;
   }
 
   llvm_unreachable("unknown module kind");
index b597c6d..5e0be1a 100644 (file)
@@ -806,7 +806,7 @@ std::pair<Module *, bool> ModuleMap::findOrCreateModule(StringRef Name,
   return std::make_pair(Result, true);
 }
 
-Module *ModuleMap::createGlobalModuleForInterfaceUnit(SourceLocation Loc) {
+Module *ModuleMap::createGlobalModuleFragmentForModuleUnit(SourceLocation Loc) {
   PendingSubmodules.emplace_back(
       new Module("<global>", Loc, nullptr, /*IsFramework*/ false,
                  /*IsExplicit*/ true, NumCreatedModules++));
@@ -814,6 +814,16 @@ Module *ModuleMap::createGlobalModuleForInterfaceUnit(SourceLocation Loc) {
   return PendingSubmodules.back().get();
 }
 
+Module *
+ModuleMap::createPrivateModuleFragmentForInterfaceUnit(Module *Parent,
+                                                       SourceLocation Loc) {
+  auto *Result =
+      new Module("<private>", Loc, Parent, /*IsFramework*/ false,
+                 /*IsExplicit*/ true, NumCreatedModules++);
+  Result->Kind = Module::PrivateModuleFragment;
+  return Result;
+}
+
 Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc,
                                                 StringRef Name,
                                                 Module *GlobalModule) {
index befe6ac..d096580 100644 (file)
@@ -857,20 +857,13 @@ void Sema::ActOnStartOfTranslationUnit() {
   }
 }
 
-/// ActOnEndOfTranslationUnit - This is called at the very end of the
-/// translation unit when EOF is reached and all but the top-level scope is
-/// popped.
-void Sema::ActOnEndOfTranslationUnit() {
-  assert(DelayedDiagnostics.getCurrentPool() == nullptr
-         && "reached end of translation unit with a pool attached?");
-
-  // If code completion is enabled, don't perform any end-of-translation-unit
-  // work.
-  if (PP.isCodeCompletionEnabled())
+void Sema::ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind) {
+  // No explicit actions are required at the end of the global module fragment.
+  if (Kind == TUFragmentKind::Global)
     return;
 
   // Transfer late parsed template instantiations over to the pending template
-  // instantiation list. During normal compliation, the late template parser
+  // instantiation list. During normal compilation, the late template parser
   // will be installed and instantiating these templates will succeed.
   //
   // If we are building a TU prefix for serialization, it is also safe to
@@ -883,50 +876,79 @@ void Sema::ActOnEndOfTranslationUnit() {
                                LateParsedInstantiations.end());
   LateParsedInstantiations.clear();
 
+  // If DefinedUsedVTables ends up marking any virtual member functions it
+  // might lead to more pending template instantiations, which we then need
+  // to instantiate.
+  DefineUsedVTables();
+
+  // C++: Perform implicit template instantiations.
+  //
+  // FIXME: When we perform these implicit instantiations, we do not
+  // carefully keep track of the point of instantiation (C++ [temp.point]).
+  // This means that name lookup that occurs within the template
+  // instantiation will always happen at the end of the translation unit,
+  // so it will find some names that are not required to be found. This is
+  // valid, but we could do better by diagnosing if an instantiation uses a
+  // name that was not visible at its first point of instantiation.
+  if (ExternalSource) {
+    // Load pending instantiations from the external source.
+    SmallVector<PendingImplicitInstantiation, 4> Pending;
+    ExternalSource->ReadPendingInstantiations(Pending);
+    for (auto PII : Pending)
+      if (auto Func = dyn_cast<FunctionDecl>(PII.first))
+        Func->setInstantiationIsPending(true);
+    PendingInstantiations.insert(PendingInstantiations.begin(),
+                                 Pending.begin(), Pending.end());
+  }
+
+  {
+    llvm::TimeTraceScope TimeScope("PerformPendingInstantiations",
+                                   StringRef(""));
+    PerformPendingInstantiations();
+  }
+
+  assert(LateParsedInstantiations.empty() &&
+         "end of TU template instantiation should not create more "
+         "late-parsed templates");
+}
+
+/// ActOnEndOfTranslationUnit - This is called at the very end of the
+/// translation unit when EOF is reached and all but the top-level scope is
+/// popped.
+void Sema::ActOnEndOfTranslationUnit() {
+  assert(DelayedDiagnostics.getCurrentPool() == nullptr
+         && "reached end of translation unit with a pool attached?");
+
+  // If code completion is enabled, don't perform any end-of-translation-unit
+  // work.
+  if (PP.isCodeCompletionEnabled())
+    return;
+
   // Complete translation units and modules define vtables and perform implicit
   // instantiations. PCH files do not.
   if (TUKind != TU_Prefix) {
     DiagnoseUseOfUnimplementedSelectors();
 
-    // If DefinedUsedVTables ends up marking any virtual member functions it
-    // might lead to more pending template instantiations, which we then need
-    // to instantiate.
-    DefineUsedVTables();
-
-    // C++: Perform implicit template instantiations.
-    //
-    // FIXME: When we perform these implicit instantiations, we do not
-    // carefully keep track of the point of instantiation (C++ [temp.point]).
-    // This means that name lookup that occurs within the template
-    // instantiation will always happen at the end of the translation unit,
-    // so it will find some names that are not required to be found. This is
-    // valid, but we could do better by diagnosing if an instantiation uses a
-    // name that was not visible at its first point of instantiation.
-    if (ExternalSource) {
-      // Load pending instantiations from the external source.
-      SmallVector<PendingImplicitInstantiation, 4> Pending;
-      ExternalSource->ReadPendingInstantiations(Pending);
-      for (auto PII : Pending)
-        if (auto Func = dyn_cast<FunctionDecl>(PII.first))
-          Func->setInstantiationIsPending(true);
-      PendingInstantiations.insert(PendingInstantiations.begin(),
-                                   Pending.begin(), Pending.end());
-    }
-
-    {
-      llvm::TimeTraceScope TimeScope("PerformPendingInstantiations",
-                                     StringRef(""));
-      PerformPendingInstantiations();
-    }
-
-    assert(LateParsedInstantiations.empty() &&
-           "end of TU template instantiation should not create more "
-           "late-parsed templates");
+    ActOnEndOfTranslationUnitFragment(
+        !ModuleScopes.empty() && ModuleScopes.back().Module->Kind ==
+                                     Module::PrivateModuleFragment
+            ? TUFragmentKind::Private
+            : TUFragmentKind::Normal);
 
     if (LateTemplateParserCleanup)
       LateTemplateParserCleanup(OpaqueParser);
 
     CheckDelayedMemberExceptionSpecs();
+  } else {
+    // If we are building a TU prefix for serialization, it is safe to transfer
+    // these over, even though they are not parsed. The end of the TU should be
+    // outside of any eager template instantiation scope, so when this AST is
+    // deserialized, these templates will not be parsed until the end of the
+    // combined TU.
+    PendingInstantiations.insert(PendingInstantiations.end(),
+                                 LateParsedInstantiations.begin(),
+                                 LateParsedInstantiations.end());
+    LateParsedInstantiations.clear();
   }
 
   DiagnoseUnterminatedPragmaPack();
@@ -1000,7 +1022,7 @@ void Sema::ActOnEndOfTranslationUnit() {
     if (getLangOpts().getCompilingModule() ==
             LangOptions::CMK_ModuleInterface &&
         (ModuleScopes.empty() ||
-         ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit) &&
+         !ModuleScopes.back().Module->isModulePurview()) &&
         !DiagnosedMissingModuleDeclaration) {
       // FIXME: Make a better guess as to where to put the module declaration.
       Diag(getSourceManager().getLocForStartOfFile(
index 4c6680c..1c33267 100644 (file)
@@ -1461,12 +1461,17 @@ bool Sema::CheckRedeclarationModuleOwnership(NamedDecl *New, NamedDecl *Old) {
 
   Module *NewM = New->getOwningModule();
   Module *OldM = Old->getOwningModule();
+
+  if (NewM && NewM->Kind == Module::PrivateModuleFragment)
+    NewM = NewM->Parent;
+  if (OldM && OldM->Kind == Module::PrivateModuleFragment)
+    OldM = OldM->Parent;
+
   if (NewM == OldM)
     return false;
 
-  // FIXME: Check proclaimed-ownership-declarations here too.
-  bool NewIsModuleInterface = NewM && NewM->Kind == Module::ModuleInterfaceUnit;
-  bool OldIsModuleInterface = OldM && OldM->Kind == Module::ModuleInterfaceUnit;
+  bool NewIsModuleInterface = NewM && NewM->isModulePurview();
+  bool OldIsModuleInterface = OldM && OldM->isModulePurview();
   if (NewIsModuleInterface || OldIsModuleInterface) {
     // C++ Modules TS [basic.def.odr] 6.2/6.7 [sic]:
     //   if a declaration of D [...] appears in the purview of a module, all
index e0439b0..43d8e47 100644 (file)
@@ -69,7 +69,7 @@ Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
   // We start in the global module; all those declarations are implicitly
   // module-private (though they do not have module linkage).
   auto &Map = PP.getHeaderSearchInfo().getModuleMap();
-  auto *GlobalModule = Map.createGlobalModuleForInterfaceUnit(ModuleLoc);
+  auto *GlobalModule = Map.createGlobalModuleFragmentForModuleUnit(ModuleLoc);
   assert(GlobalModule && "module creation should not fail");
 
   // Enter the scope of the global module.
@@ -128,7 +128,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
 
   // Only one module-declaration is permitted per source file.
   if (!ModuleScopes.empty() &&
-      ModuleScopes.back().Module->Kind == Module::ModuleInterfaceUnit) {
+      ModuleScopes.back().Module->isModulePurview()) {
     Diag(ModuleLoc, diag::err_module_redeclaration);
     Diag(VisibleModules.getImportLoc(ModuleScopes.back().Module),
          diag::note_prev_module_declaration);
@@ -220,6 +220,9 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
     ModuleScopes.push_back({});
     if (getLangOpts().ModulesLocalVisibility)
       ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules);
+  } else {
+    // We're done with the global module fragment now.
+    ActOnEndOfTranslationUnitFragment(TUFragmentKind::Global);
   }
 
   // Switch from the global module fragment (if any) to the named module.
@@ -239,6 +242,68 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
   return nullptr;
 }
 
+Sema::DeclGroupPtrTy
+Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc,
+                                     SourceLocation PrivateLoc) {
+  // C++20 [basic.link]/2:
+  //   A private-module-fragment shall appear only in a primary module
+  //   interface unit.
+  switch (ModuleScopes.empty() ? Module::GlobalModuleFragment
+                               : ModuleScopes.back().Module->Kind) {
+  case Module::ModuleMapModule:
+  case Module::GlobalModuleFragment:
+    Diag(PrivateLoc, diag::err_private_module_fragment_not_module);
+    return nullptr;
+
+  case Module::PrivateModuleFragment:
+    Diag(PrivateLoc, diag::err_private_module_fragment_redefined);
+    Diag(ModuleScopes.back().BeginLoc, diag::note_previous_definition);
+    return nullptr;
+
+  case Module::ModuleInterfaceUnit:
+    break;
+  }
+
+  if (!ModuleScopes.back().ModuleInterface) {
+    Diag(PrivateLoc, diag::err_private_module_fragment_not_module_interface);
+    Diag(ModuleScopes.back().BeginLoc,
+         diag::note_not_module_interface_add_export)
+        << FixItHint::CreateInsertion(ModuleScopes.back().BeginLoc, "export ");
+    return nullptr;
+  }
+
+  // FIXME: Check this isn't a module interface partition.
+  // FIXME: Check that this translation unit does not import any partitions;
+  // such imports would violate [basic.link]/2's "shall be the only module unit"
+  // restriction.
+
+  // We've finished the public fragment of the translation unit.
+  ActOnEndOfTranslationUnitFragment(TUFragmentKind::Normal);
+
+  auto &Map = PP.getHeaderSearchInfo().getModuleMap();
+  Module *PrivateModuleFragment =
+      Map.createPrivateModuleFragmentForInterfaceUnit(
+          ModuleScopes.back().Module, PrivateLoc);
+  assert(PrivateModuleFragment && "module creation should not fail");
+
+  // Enter the scope of the private module fragment.
+  ModuleScopes.push_back({});
+  ModuleScopes.back().BeginLoc = ModuleLoc;
+  ModuleScopes.back().Module = PrivateModuleFragment;
+  ModuleScopes.back().ModuleInterface = true;
+  VisibleModules.setVisible(PrivateModuleFragment, ModuleLoc);
+
+  // All declarations created from now on are scoped to the private module
+  // fragment (and are neither visible nor reachable in importers of the module
+  // interface).
+  auto *TU = Context.getTranslationUnitDecl();
+  TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate);
+  TU->setLocalOwningModule(PrivateModuleFragment);
+
+  // FIXME: Consider creating an explicit representation of this declaration.
+  return nullptr;
+}
+
 DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
                                    SourceLocation ExportLoc,
                                    SourceLocation ImportLoc,
@@ -451,17 +516,26 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc,
                                  SourceLocation LBraceLoc) {
   ExportDecl *D = ExportDecl::Create(Context, CurContext, ExportLoc);
 
-  // C++ Modules TS draft:
-  //   An export-declaration shall appear in the purview of a module other than
-  //   the global module.
-  if (ModuleScopes.empty() || !ModuleScopes.back().ModuleInterface)
-    Diag(ExportLoc, diag::err_export_not_in_module_interface);
+  // C++20 [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.
+  if (ModuleScopes.empty() || !ModuleScopes.back().Module->isModulePurview()) {
+    Diag(ExportLoc, diag::err_export_not_in_module_interface) << 0;
+  } else if (!ModuleScopes.back().ModuleInterface) {
+    Diag(ExportLoc, diag::err_export_not_in_module_interface) << 1;
+    Diag(ModuleScopes.back().BeginLoc,
+         diag::note_not_module_interface_add_export)
+        << FixItHint::CreateInsertion(ModuleScopes.back().BeginLoc, "export ");
+  } else if (ModuleScopes.back().Module->Kind ==
+             Module::PrivateModuleFragment) {
+    Diag(ExportLoc, diag::err_export_in_private_module_fragment);
+    Diag(ModuleScopes.back().BeginLoc, diag::note_private_module_fragment);
+  }
 
-  //   An export-declaration [...] shall not contain more than one
-  //   export keyword.
-  //
-  // The intent here is that an export-declaration cannot appear within another
-  // export-declaration.
+  //   [...] its declaration or declaration-seq shall not contain an
+  //   export-declaration.
   if (D->isExported())
     Diag(ExportLoc, diag::err_export_within_export);
 
index b94e570..c6a119a 100644 (file)
@@ -1,24 +1,31 @@
 // RUN: %clang_cc1 -std=c++2a -verify %s
 // RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG %s
 // RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_PRIVATE_FRAG %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_PRIVATE_FRAG %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL %s
+// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s
 // RUN: %clang_cc1 -std=c++2a -verify -DEXPORT_FRAGS %s
 
-#ifdef NO_GLOBAL_FRAG
-// expected-error@#mod-decl {{module declaration must occur at the start of the translation unit}}
-// expected-note@1 {{add 'module;' to the start of the file to introduce a global module fragment}}
-#else
+#ifndef NO_GLOBAL_FRAG
 #ifdef EXPORT_FRAGS
 export // expected-error {{global module fragment cannot be exported}}
 #endif
-module; // #glob-frag
+module;
+#ifdef NO_MODULE_DECL
+// expected-error@-2 {{missing 'module' declaration at end of global module fragment introduced here}}
+#endif
 #endif
 
 extern int a; // #a1
 
-#ifdef NO_MODULE_DECL
-// expected-error@#glob-frag {{missing 'module' declaration at end of global module fragment introduced here}}
-#else
-export module Foo; // #mod-decl
+#ifndef NO_MODULE_DECL
+export module Foo;
+#ifdef NO_GLOBAL_FRAG
+// expected-error@-2 {{module declaration must occur at the start of the translation unit}}
+// expected-note@1 {{add 'module;' to the start of the file to introduce a global module fragment}}
+#endif
 
 // expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
 // expected-note@#a1 {{previous decl}}
@@ -29,9 +36,22 @@ extern int b;
 
 module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
 
+#ifndef NO_PRIVATE_FRAG
 #ifdef EXPORT_FRAGS
 export // expected-error {{private module fragment cannot be exported}}
 #endif
-module :private;
+module :private; // #priv-frag
+#ifdef NO_MODULE_DECL
+// expected-error@-2 {{private module fragment declaration with no preceding module declaration}}
+#endif
+#endif
 
 int b; // ok
+
+
+#ifndef NO_PRIVATE_FRAG
+#ifndef NO_MODULE_DECL
+module :private; // expected-error {{private module fragment redefined}}
+// expected-note@#priv-frag {{previous definition is here}}
+#endif
+#endif
diff --git a/clang/test/CXX/basic/basic.link/p2.cpp b/clang/test/CXX/basic/basic.link/p2.cpp
new file mode 100644 (file)
index 0000000..54e347c
--- /dev/null
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -verify
+// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -emit-module-interface -o %t.pcm
+// RUN: %clang_cc1 -std=c++2a -UEXPORT %s -verify -fmodule-file=%t.pcm
+
+#ifdef EXPORT
+// expected-no-diagnostics
+export
+#else
+// expected-note@+2 {{add 'export' here}}
+#endif
+module M;
+
+#ifndef EXPORT
+// expected-error@+2 {{private module fragment in module implementation unit}}
+#endif
+module :private;
diff --git a/clang/test/CXX/module/module.interface/p1.cpp b/clang/test/CXX/module/module.interface/p1.cpp
new file mode 100644 (file)
index 0000000..1eba817
--- /dev/null
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -std=c++2a %s -DERRORS -verify
+// RUN: %clang_cc1 -std=c++2a %s -emit-module-interface -o %t.pcm
+// RUN: %clang_cc1 -std=c++2a %s -fmodule-file=%t.pcm -DIMPLEMENTATION -verify -Db=b2 -Dc=c2
+
+module;
+
+#ifdef ERRORS
+export int a; // expected-error {{after the module declaration}}
+#endif
+
+#ifndef IMPLEMENTATION
+export
+#else
+// expected-error@#1 {{can only be used within a module interface unit}}
+// expected-error@#2 {{can only be used within a module interface unit}}
+// expected-note@+2 1+{{add 'export'}}
+#endif
+module M;
+
+export int b; // #1
+namespace N {
+  export int c; // #2
+}
+
+#ifdef ERRORS
+namespace {
+  export int d1; // FIXME: invalid
+  namespace X {
+    export int d2; // FIXME: invalid
+  }
+}
+
+export export int e; // expected-error {{within another export declaration}}
+export { export int f; } // expected-error {{within another export declaration}}
+
+module :private; // expected-note {{private module fragment begins here}}
+export int priv; // expected-error {{export declaration cannot be used in a private module fragment}}
+#endif
index 68f2570..52f45f5 100644 (file)
@@ -10,7 +10,7 @@
 // expected-no-diagnostics
 export module A;
 #elif IMPLEMENTATION
-module A;
+module A; // #module-decl
  #ifdef BUILT_AS_INTERFACE
   // expected-error@-2 {{missing 'export' specifier in module declaration while building module interface}}
   #define INTERFACE
@@ -23,6 +23,9 @@ module A;
 
 #ifndef INTERFACE
 export int b; // expected-error {{export declaration can only be used within a module interface unit}}
+#ifdef IMPLEMENTATION
+// expected-note@#module-decl {{add 'export' here}}
+#endif
 #else
 export int a;
 #endif