[clang] 69350e5 - [C++20][Modules][3/8] Initial handling for module partitions.

Iain Sandoe via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 24 01:01:27 PST 2022


Author: Iain Sandoe
Date: 2022-02-24T09:01:09Z
New Revision: 69350e569dc47f871590243b5e46a68520640dcd

URL: https://github.com/llvm/llvm-project/commit/69350e569dc47f871590243b5e46a68520640dcd
DIFF: https://github.com/llvm/llvm-project/commit/69350e569dc47f871590243b5e46a68520640dcd.diff

LOG: [C++20][Modules][3/8] Initial handling for module partitions.

This implements the parsing and recognition of module partition CMIs
and removes the FIXMEs in the parser.

Module partitions are recognised in the base computation of visibility,
however additional amendments to visibility follow in subsequent patches.

Differential Revision: https://reviews.llvm.org/D118586

Added: 
    clang/test/Modules/cxx20-multiple-partitions.cpp
    clang/test/Modules/cxx20-partition-diagnostics-a.cpp

Modified: 
    clang/include/clang/AST/Decl.h
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Sema/Sema.h
    clang/lib/Parse/Parser.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaLookup.cpp
    clang/lib/Sema/SemaModule.cpp
    clang/test/CXX/module/module.unit/p3.cpp
    clang/test/CXX/module/module.unit/p8.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 7611bac83419d..926426b5470d2 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -4467,6 +4467,16 @@ class CapturedDecl final
 ///   @import std.vector;
 /// \endcode
 ///
+/// A C++20 module import declaration imports the named module or partition.
+/// Periods are permitted in C++20 module names, but have no semantic meaning.
+/// For example:
+/// \code
+///   import NamedModule;
+///   import :SomePartition; // Must be a partition of the current module.
+///   import Names.Like.this; // Allowed.
+///   import :and.Also.Partition.names;
+/// \endcode
+///
 /// Import declarations can also be implicitly generated from
 /// \#include/\#import directives.
 class ImportDecl final : public Decl,

diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index f7d9a685e8b92..92ae27d6212a0 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1541,9 +1541,11 @@ def err_private_module_fragment_expected_semi : Error<
   "expected ';' after private module fragment declaration">;
 def err_missing_before_module_end : Error<"expected %0 at end of module">;
 def err_unsupported_module_partition : Error<
-  "sorry, module partitions are not yet supported">;
+  "module partitions are only supported for C++20 onwards">;
 def err_import_not_allowed_here : Error<
   "imports must immediately follow the module declaration">;
+def err_partition_import_outside_module : Error<
+  "module partition imports must be within a module purview">;
 def err_import_in_wrong_fragment : Error<
   "module%select{| partition}0 imports cannot be in the %select{global|private}1 module fragment">;
 

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 94283e2412a98..436718a54640e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2210,6 +2210,7 @@ class Sema final {
     SourceLocation BeginLoc;
     clang::Module *Module = nullptr;
     bool ModuleInterface = false;
+    bool IsPartition = false;
     bool ImplicitGlobalModuleFragment = false;
     VisibleModuleSet OuterVisibleModules;
   };
@@ -2962,7 +2963,7 @@ class Sema final {
   /// of a module interface or implementation.
   DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
                                  SourceLocation ModuleLoc, ModuleDeclKind MDK,
-                                 ModuleIdPath Path,
+                                 ModuleIdPath Path, ModuleIdPath Partition,
                                  ModuleImportState &ImportState);
 
   /// The parser has processed a global-module-fragment declaration that begins
@@ -2983,10 +2984,12 @@ class Sema final {
   ///        could be the location of an '@', 'export', or 'import'.
   /// \param ExportLoc The location of the 'export' keyword, if any.
   /// \param ImportLoc The location of the 'import' keyword.
-  /// \param Path The module access path.
+  /// \param Path The module toplevel name as an access path.
+  /// \param Partition The module partition name as an access path.
   DeclResult ActOnModuleImport(SourceLocation StartLoc,
                                SourceLocation ExportLoc,
-                               SourceLocation ImportLoc, ModuleIdPath Path);
+                               SourceLocation ImportLoc, ModuleIdPath Path,
+                               ModuleIdPath Partition = {});
   DeclResult ActOnModuleImport(SourceLocation StartLoc,
                                SourceLocation ExportLoc,
                                SourceLocation ImportLoc, Module *M,

diff  --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 87500a0405531..b7e4be6e436e3 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -2364,20 +2364,19 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
   }
 
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
-  if (ParseModuleName(ModuleLoc, Path, /*IsImport*/false))
+  if (ParseModuleName(ModuleLoc, Path, /*IsImport*/ false))
     return nullptr;
 
   // Parse the optional module-partition.
+  SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
   if (Tok.is(tok::colon)) {
     SourceLocation ColonLoc = ConsumeToken();
-    SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
-    if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/false))
+    if (!getLangOpts().CPlusPlusModules)
+      Diag(ColonLoc, diag::err_unsupported_module_partition)
+          << SourceRange(ColonLoc, Partition.back().second);
+    // Recover by ignoring the partition name.
+    else if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/ false))
       return nullptr;
-
-    // FIXME: Support module partition declarations.
-    Diag(ColonLoc, diag::err_unsupported_module_partition)
-      << SourceRange(ColonLoc, Partition.back().second);
-    // Recover by parsing as a non-partition.
   }
 
   // We don't support any module attributes yet; just parse them and diagnose.
@@ -2387,18 +2386,19 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
 
   ExpectAndConsumeSemi(diag::err_module_expected_semi);
 
-  return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, ImportState);
+  return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition,
+                                 ImportState);
 }
 
 /// Parse a module import declaration. This is essentially the same for
-/// Objective-C and the C++ Modules TS, except for the leading '@' (in ObjC)
-/// and the trailing optional attributes (in C++).
+/// Objective-C and C++20 except for the leading '@' (in ObjC) and the
+/// trailing optional attributes (in C++).
 ///
 /// [ObjC]  @import declaration:
 ///           '@' 'import' module-name ';'
 /// [ModTS] module-import-declaration:
 ///           'import' module-name attribute-specifier-seq[opt] ';'
-/// [C++2a] module-import-declaration:
+/// [C++20] module-import-declaration:
 ///           'export'[opt] 'import' module-name
 ///                   attribute-specifier-seq[opt] ';'
 ///           'export'[opt] 'import' module-partition
@@ -2418,9 +2418,10 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
   bool IsObjCAtImport = Tok.isObjCAtKeyword(tok::objc_import);
   SourceLocation ImportLoc = ConsumeToken();
 
+  // For C++20 modules, we can have "name" or ":Partition name" as valid input.
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
+  SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
   Module *HeaderUnit = nullptr;
-
   if (Tok.is(tok::header_name)) {
     // This is a header import that the preprocessor decided we should skip
     // because it was malformed in some way. Parse and ignore it; it's already
@@ -2430,17 +2431,16 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
     // This is a header import that the preprocessor mapped to a module import.
     HeaderUnit = reinterpret_cast<Module *>(Tok.getAnnotationValue());
     ConsumeAnnotationToken();
-  } else if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon)) {
+  } else if (Tok.is(tok::colon)) {
     SourceLocation ColonLoc = ConsumeToken();
-    if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
+    if (!getLangOpts().CPlusPlusModules)
+      Diag(ColonLoc, diag::err_unsupported_module_partition)
+          << SourceRange(ColonLoc, Partition.back().second);
+    // Recover by leaving partition empty.
+    else if (ParseModuleName(ColonLoc, Partition, /*IsImport*/ true))
       return nullptr;
-
-    // FIXME: Support module partition import.
-    Diag(ColonLoc, diag::err_unsupported_module_partition)
-      << SourceRange(ColonLoc, Path.back().second);
-    return nullptr;
   } else {
-    if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
+    if (ParseModuleName(ImportLoc, Path, /*IsImport*/ true))
       return nullptr;
   }
 
@@ -2457,21 +2457,24 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
 
   // Diagnose mis-imports.
   bool SeenError = true;
+  bool HasPart = !Partition.empty();
   switch (ImportState) {
   case Sema::ModuleImportState::ImportAllowed:
     SeenError = false;
     break;
   case Sema::ModuleImportState::FirstDecl:
   case Sema::ModuleImportState::NotACXX20Module:
-    // TODO: These cases will be an error when partitions are implemented.
-    SeenError = false;
+    // We can only import a partition within a module purview.
+    if (HasPart)
+      Diag(ImportLoc, diag::err_partition_import_outside_module);
+    else
+      SeenError = false;
     break;
   case Sema::ModuleImportState::GlobalFragment:
     // We can only have pre-processor directives in the global module
     // fragment.  We can, however have a header unit import here.
     if (!HeaderUnit)
-      // We do not have partition support yet, so first arg is 0.
-      Diag(ImportLoc, diag::err_import_in_wrong_fragment) << 0 << 0;
+      Diag(ImportLoc, diag::err_import_in_wrong_fragment) << HasPart << 0;
     else
       SeenError = false;
     break;
@@ -2482,8 +2485,7 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
       SeenError = false;
     break;
   case Sema::ModuleImportState::PrivateFragment:
-    // We do not have partition support yet, so first arg is 0.
-    Diag(ImportLoc, diag::err_import_in_wrong_fragment) << 0 << 1;
+    Diag(ImportLoc, diag::err_import_in_wrong_fragment) << HasPart << 1;
     break;
   }
   if (SeenError) {
@@ -2495,8 +2497,9 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
   if (HeaderUnit)
     Import =
         Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit);
-  else if (!Path.empty())
-    Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path);
+  else if (!Path.empty() || !Partition.empty())
+    Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path,
+                                       Partition);
   ExpectAndConsumeSemi(diag::err_module_expected_semi);
   if (Import.isInvalid())
     return nullptr;

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 71c5fa5509760..52d9a23386914 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -1608,6 +1608,14 @@ bool Sema::CheckRedeclarationModuleOwnership(NamedDecl *New, NamedDecl *Old) {
   if (OldM && OldM->Kind == Module::PrivateModuleFragment)
     OldM = OldM->Parent;
 
+  // If we have a decl in a module partition, it is part of the containing
+  // module (which is the only thing that can be importing it).
+  if (NewM && OldM &&
+      (OldM->Kind == Module::ModulePartitionInterface ||
+       OldM->Kind == Module::ModulePartitionImplementation)) {
+    return false;
+  }
+
   if (NewM == OldM)
     return false;
 

diff  --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index af6ee24240ceb..13320497c4b4b 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -1561,8 +1561,12 @@ llvm::DenseSet<Module*> &Sema::getLookupModules() {
 static bool isInCurrentModule(const Module *M, const LangOptions &LangOpts) {
   // If M is the global module fragment of a module that we've not yet finished
   // parsing, then it must be part of the current module.
+  // If it's a partition, then it must be visible to an importer (since only
+  // another partition or the named module can import it).
   return M->getTopLevelModuleName() == LangOpts.CurrentModule ||
-         (M->Kind == Module::GlobalModuleFragment && !M->Parent);
+         (M->Kind == Module::GlobalModuleFragment && !M->Parent) ||
+         M->Kind == Module::ModulePartitionInterface ||
+         M->Kind == Module::ModulePartitionImplementation;
 }
 
 bool Sema::hasVisibleMergedDefinition(NamedDecl *Def) {

diff  --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index bd5b900e5d389..125dbefd9dd07 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -54,6 +54,23 @@ static void checkModuleImportContext(Sema &S, Module *M,
   }
 }
 
+// We represent the primary and partition names as 'Paths' which are sections
+// of the hierarchical access path for a clang module.  However for C++20
+// the periods in a name are just another character, and we will need to
+// flatten them into a string.
+static std::string stringFromPath(ModuleIdPath Path) {
+  std::string Name;
+  if (Path.empty())
+    return Name;
+
+  for (auto &Piece : Path) {
+    if (!Name.empty())
+      Name += ".";
+    Name += Piece.first->getName();
+  }
+  return Name;
+}
+
 Sema::DeclGroupPtrTy
 Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
   if (!ModuleScopes.empty() &&
@@ -80,11 +97,10 @@ Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
   return nullptr;
 }
 
-Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
-                                           SourceLocation ModuleLoc,
-                                           ModuleDeclKind MDK,
-                                           ModuleIdPath Path,
-                                           ModuleImportState &ImportState) {
+Sema::DeclGroupPtrTy
+Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
+                      ModuleDeclKind MDK, ModuleIdPath Path,
+                      ModuleIdPath Partition, ModuleImportState &ImportState) {
   assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) &&
          "should only have module decl in Modules TS or C++20");
 
@@ -163,19 +179,20 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
   // Flatten the dots in a module name. Unlike Clang's hierarchical module map
   // modules, the dots here are just another character that can appear in a
   // module name.
-  std::string ModuleName;
-  for (auto &Piece : Path) {
-    if (!ModuleName.empty())
-      ModuleName += ".";
-    ModuleName += Piece.first->getName();
+  std::string ModuleName = stringFromPath(Path);
+  bool IsPartition = !Partition.empty();
+  if (IsPartition) {
+    ModuleName += ":";
+    ModuleName += stringFromPath(Partition);
   }
-
   // If a module name was explicitly specified on the command line, it must be
   // correct.
   if (!getLangOpts().CurrentModule.empty() &&
       getLangOpts().CurrentModule != ModuleName) {
     Diag(Path.front().second, diag::err_current_module_name_mismatch)
-        << SourceRange(Path.front().second, Path.back().second)
+        << SourceRange(Path.front().second, IsPartition
+                                                ? Partition.back().second
+                                                : Path.back().second)
         << getLangOpts().CurrentModule;
     return nullptr;
   }
@@ -202,6 +219,8 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
     // Create a Module for the module that we're defining.
     Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
                                            GlobalModuleFragment);
+    if (IsPartition)
+      Mod->Kind = Module::ModulePartitionInterface;
     assert(Mod && "module creation should not fail");
     break;
   }
@@ -209,14 +228,26 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
   case ModuleDeclKind::Implementation:
     std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc(
         PP.getIdentifierInfo(ModuleName), Path[0].second);
-    Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc},
-                                       Module::AllVisible,
-                                       /*IsInclusionDirective=*/false);
-    if (!Mod) {
-      Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName;
-      // Create an empty module interface unit for error recovery.
+    if (IsPartition) {
+      // Create an interface, but note that it is an implementation
+      // unit.
       Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
                                              GlobalModuleFragment);
+      Mod->Kind = Module::ModulePartitionImplementation;
+    } else {
+      // C++20 A module-declaration that contains neither an export-
+      // keyword nor a module-partition implicitly imports the primary
+      // module interface unit of the module as if by a module-import-
+      // declaration.
+      Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc},
+                                         Module::AllVisible,
+                                         /*IsInclusionDirective=*/false);
+      if (!Mod) {
+        Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName;
+        // Create an empty module interface unit for error recovery.
+        Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
+                                               GlobalModuleFragment);
+      }
     }
     break;
   }
@@ -233,7 +264,9 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
   // Switch from the global module fragment (if any) to the named module.
   ModuleScopes.back().BeginLoc = StartLoc;
   ModuleScopes.back().Module = Mod;
-  ModuleScopes.back().ModuleInterface = MDK != ModuleDeclKind::Implementation;
+  ModuleScopes.back().ModuleInterface =
+      (MDK != ModuleDeclKind::Implementation || IsPartition);
+  ModuleScopes.back().IsPartition = IsPartition;
   VisibleModules.setVisible(Mod, ModuleLoc);
 
   // From now on, we have an owning module for all declarations we see.
@@ -317,17 +350,40 @@ Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc,
 
 DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
                                    SourceLocation ExportLoc,
-                                   SourceLocation ImportLoc,
-                                   ModuleIdPath Path) {
-  // Flatten the module path for a C++20 or Modules TS module name.
+                                   SourceLocation ImportLoc, ModuleIdPath Path,
+                                   ModuleIdPath Partition) {
+
+  bool IsPartition = !Partition.empty();
+  bool Cxx20Mode = getLangOpts().CPlusPlusModules || getLangOpts().ModulesTS;
+  assert((!IsPartition || Cxx20Mode) && "partition seen in non-C++20 code?");
+  assert((!IsPartition || Path.empty()) &&
+         "trying to import a partition with its named module specified?");
+
+  // For a C++20 module name, flatten into a single identifier with the source
+  // location of the first component.
   std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc;
+
   std::string ModuleName;
-  if (getLangOpts().CPlusPlusModules || getLangOpts().ModulesTS) {
-    for (auto &Piece : Path) {
-      if (!ModuleName.empty())
-        ModuleName += ".";
-      ModuleName += Piece.first->getName();
+  if (IsPartition) {
+    // We already checked that we are in a module purview in the parser.
+    assert(!ModuleScopes.empty() && "in a module purview, but no module?");
+    Module *NamedMod = ModuleScopes.back().Module;
+    if (ModuleScopes.back().IsPartition) {
+      // We're importing a partition into a partition, find the name of the
+      // owning named module.
+      size_t P = NamedMod->Name.find_first_of(":");
+      ModuleName = NamedMod->Name.substr(0, P + 1);
+    } else {
+      // We're importing a partition into the named module itself (either the
+      // interface or an implementation TU).
+      ModuleName = NamedMod->Name;
+      ModuleName += ":";
     }
+    ModuleName += stringFromPath(Partition);
+    ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Partition[0].second};
+    Partition = ModuleIdPath(ModuleNameLoc);
+  } else if (Cxx20Mode) {
+    ModuleName = stringFromPath(Path);
     ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Path[0].second};
     Path = ModuleIdPath(ModuleNameLoc);
   }
@@ -340,13 +396,14 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
     return true;
   }
 
-  Module *Mod =
-      getModuleLoader().loadModule(ImportLoc, Path, Module::AllVisible,
-                                   /*IsInclusionDirective=*/false);
+  Module *Mod = getModuleLoader().loadModule(
+      ImportLoc, IsPartition ? Partition : Path, Module::AllVisible,
+      /*IsInclusionDirective=*/false);
   if (!Mod)
     return true;
 
-  return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path);
+  return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod,
+                           IsPartition ? Partition : Path);
 }
 
 /// Determine whether \p D is lexically within an export-declaration.
@@ -359,8 +416,8 @@ static const ExportDecl *getEnclosingExportDecl(const Decl *D) {
 
 DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
                                    SourceLocation ExportLoc,
-                                   SourceLocation ImportLoc,
-                                   Module *Mod, ModuleIdPath Path) {
+                                   SourceLocation ImportLoc, Module *Mod,
+                                   ModuleIdPath Path) {
   VisibleModules.setVisible(Mod, ImportLoc);
 
   checkModuleImportContext(*this, Mod, ImportLoc, CurContext);
@@ -379,22 +436,26 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
   }
 
   SmallVector<SourceLocation, 2> IdentifierLocs;
-  Module *ModCheck = Mod;
-  for (unsigned I = 0, N = Path.size(); I != N; ++I) {
-    // If we've run out of module parents, just drop the remaining identifiers.
-    // We need the length to be consistent.
-    if (!ModCheck)
-      break;
-    ModCheck = ModCheck->Parent;
-
-    IdentifierLocs.push_back(Path[I].second);
-  }
 
-  // If this was a header import, pad out with dummy locations.
-  // FIXME: Pass in and use the location of the header-name token in this case.
   if (Path.empty()) {
-    for (; ModCheck; ModCheck = ModCheck->Parent) {
+    // If this was a header import, pad out with dummy locations.
+    // FIXME: Pass in and use the location of the header-name token in this
+    // case.
+    for (Module *ModCheck = Mod; ModCheck; ModCheck = ModCheck->Parent)
       IdentifierLocs.push_back(SourceLocation());
+  } else if (getLangOpts().CPlusPlusModules && !Mod->Parent) {
+    // A single identifier for the whole name.
+    IdentifierLocs.push_back(Path[0].second);
+  } else {
+    Module *ModCheck = Mod;
+    for (unsigned I = 0, N = Path.size(); I != N; ++I) {
+      // If we've run out of module parents, just drop the remaining
+      // identifiers.  We need the length to be consistent.
+      if (!ModCheck)
+        break;
+      ModCheck = ModCheck->Parent;
+
+      IdentifierLocs.push_back(Path[I].second);
     }
   }
 
@@ -420,6 +481,10 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
     // An export-declaration shall inhabit a namespace scope and appear in the
     // purview of a module interface unit.
     Diag(ExportLoc, diag::err_export_not_in_module_interface) << 0;
+  } else if (getLangOpts().isCompilingModule()) {
+    Module *ThisModule = PP.getHeaderSearchInfo().lookupModule(
+        getLangOpts().CurrentModule, ExportLoc, false, false);
+    assert(ThisModule && "was expecting a module if building one");
   }
 
   return Import;
@@ -457,6 +522,12 @@ void Sema::BuildModuleInclude(SourceLocation DirectiveLoc, Module *Mod) {
 
   getModuleLoader().makeModuleVisible(Mod, Module::AllVisible, DirectiveLoc);
   VisibleModules.setVisible(Mod, DirectiveLoc);
+
+  if (getLangOpts().isCompilingModule()) {
+    Module *ThisModule = PP.getHeaderSearchInfo().lookupModule(
+        getLangOpts().CurrentModule, DirectiveLoc, false, false);
+    assert(ThisModule && "was expecting a module if building one");
+  }
 }
 
 void Sema::ActOnModuleBegin(SourceLocation DirectiveLoc, Module *Mod) {
@@ -757,8 +828,9 @@ Module *Sema::PushGlobalModuleFragment(SourceLocation BeginLoc,
   // Enter the scope of the global module.
   ModuleScopes.push_back({BeginLoc, GlobalModuleFragment,
                           /*ModuleInterface=*/false,
+                          /*IsPartition=*/false,
                           /*ImplicitGlobalModuleFragment=*/IsImplicit,
-                          /*VisibleModuleSet*/ {}});
+                          /*OuterVisibleModules=*/{}});
   VisibleModules.setVisible(GlobalModuleFragment, BeginLoc);
 
   return GlobalModuleFragment;

diff  --git a/clang/test/CXX/module/module.unit/p3.cpp b/clang/test/CXX/module/module.unit/p3.cpp
index 2e08f4a9a80f0..ae81b987b52bb 100644
--- a/clang/test/CXX/module/module.unit/p3.cpp
+++ b/clang/test/CXX/module/module.unit/p3.cpp
@@ -1,4 +1,4 @@
 // RUN: %clang_cc1 -std=c++2a -verify %s
 
-export module foo:bar; // expected-error {{sorry, module partitions are not yet supported}}
-import :baz; // expected-error {{sorry, module partitions are not yet supported}}
+export module foo:bar;
+import :baz; // expected-error {{module 'foo:baz' not found}}

diff  --git a/clang/test/CXX/module/module.unit/p8.cpp b/clang/test/CXX/module/module.unit/p8.cpp
index a4a85d0bf91fe..e22da164febf1 100644
--- a/clang/test/CXX/module/module.unit/p8.cpp
+++ b/clang/test/CXX/module/module.unit/p8.cpp
@@ -12,7 +12,7 @@
 
 #elif MODE == 1
 // expected-no-diagnostics
-module foo;
+module foo; // Implementation, implicitly imports foo.
 #define IMPORTED
 
 #elif MODE == 2
@@ -21,15 +21,15 @@ export module foo; // expected-error {{redefinition of module 'foo'}}
 #define IMPORTED
 
 #elif MODE == 3
-export module bar;
+export module bar; // A 
diff erent module
 
 #elif MODE == 4
-module foo:bar; // expected-error {{not yet supported}}
-#define IMPORTED // FIXME
+module foo:bar; // Partition implementation
+//#define IMPORTED (we don't import foo here)
 
 #elif MODE == 5
-export module foo:bar; // expected-error {{not yet supported}} expected-error {{redefinition}} expected-note@* {{loaded from}}
-#define IMPORTED // FIXME
+export module foo:bar; // Partition interface
+//#define IMPORTED  (we don't import foo here)
 
 #endif
 

diff  --git a/clang/test/Modules/cxx20-multiple-partitions.cpp b/clang/test/Modules/cxx20-multiple-partitions.cpp
new file mode 100644
index 0000000000000..00a90929b504c
--- /dev/null
+++ b/clang/test/Modules/cxx20-multiple-partitions.cpp
@@ -0,0 +1,46 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/partition1.cpp \
+// RUN:  -o %t/A_part1.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/partition2.cpp \
+// RUN:  -o %t/A_part2.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/partition3.cpp \
+// RUN:  -o %t/A_part3.pcm
+
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/moduleA.cpp \
+// RUN:  -fmodule-file=%t/A_part1.pcm -fmodule-file=%t/A_part2.pcm \
+// RUN:  -fmodule-file=%t/A_part3.pcm -o %t/A.pcm
+
+// expected-no-diagnostics
+
+//--- partition1.cpp
+
+export module A:Part1;
+
+int part1();
+
+//--- partition2.cpp
+
+export module A:Part2;
+
+int part2();
+
+//--- partition3.cpp
+
+export module A:Part3;
+
+int part3();
+
+//--- moduleA.cpp
+
+export module A;
+
+import :Part1;
+export import :Part2;
+import :Part3;
+
+int foo();

diff  --git a/clang/test/Modules/cxx20-partition-diagnostics-a.cpp b/clang/test/Modules/cxx20-partition-diagnostics-a.cpp
new file mode 100644
index 0000000000000..7d7e639137385
--- /dev/null
+++ b/clang/test/Modules/cxx20-partition-diagnostics-a.cpp
@@ -0,0 +1,19 @@
+// Module Partition diagnostics
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/bad-import.cpp -verify
+
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/bad-partition.cpp -verify
+
+//--- bad-import.cpp
+
+import :B; // expected-error {{module partition imports must be within a module purview}}
+
+//--- bad-partition.cpp
+
+module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
+
+import :Part; // expected-error {{module partition imports cannot be in the global module fragment}}


        


More information about the cfe-commits mailing list