]> granicus.if.org Git - clang/commitdiff
[c++20] Parsing support for module-declarations, import-declarations,
authorRichard Smith <richard-llvm@metafoo.co.uk>
Sun, 14 Apr 2019 08:06:59 +0000 (08:06 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Sun, 14 Apr 2019 08:06:59 +0000 (08:06 +0000)
and the global and private module fragment.

For now, the private module fragment introducer is ignored, but use of
the global module fragment introducer should be properly enforced.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@358353 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Lex/Preprocessor.h
include/clang/Parse/Parser.h
include/clang/Sema/Sema.h
lib/Lex/PPDirectives.cpp
lib/Lex/Preprocessor.cpp
lib/Parse/Parser.cpp
lib/Sema/Sema.cpp
lib/Sema/SemaDecl.cpp
test/CXX/basic/basic.link/p1.cpp [new file with mode: 0644]
test/CXX/basic/basic.link/p3.cpp [new file with mode: 0644]
test/CXX/module/module.unit/p3.cpp [new file with mode: 0644]
test/CXX/modules-ts/basic/basic.link/module-declaration.cpp

index 84c24836f7d837e87a77565506d6d46708767abc..3003d0a8d339657c914e8b027e494cd034ecfbd6 100644 (file)
@@ -1223,15 +1223,22 @@ def err_unexpected_module_decl : Error<
   "module declaration can only appear at the top level">;
 def err_module_expected_ident : Error<
   "expected a module name after '%select{module|import}0'">;
-def err_module_implementation_partition : Error<
-  "module partition must be declared 'export'">;
 def err_attribute_not_module_attr : Error<
   "%0 attribute cannot be applied to a module">;
 def err_attribute_not_import_attr : Error<
   "%0 attribute cannot be applied to a module import">;
 def err_module_expected_semi : Error<
   "expected ';' after module name">;
+def err_global_module_introducer_not_at_start : Error<
+  "'module;' introducing a global module fragment can appear only "
+  "at the start of the translation unit">;
+def err_module_fragment_exported : Error<
+  "%select{global|private}0 module fragment cannot be exported">;
+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">;
 
 def err_export_empty : Error<"export declaration cannot be empty">;
 }
index 6b824da36021a0a785368a262065a446813150fd..18792c10b759aa1d30356d4539671f7f14f4c280 100644 (file)
@@ -9199,7 +9199,7 @@ let CategoryName = "Modules Issue" in {
 def err_module_decl_in_module_map_module : Error<
   "'module' declaration found while building module from module map">;
 def err_module_decl_in_header_module : Error<
-  "'module' declaration found while building header module">;
+  "'module' declaration found while building header unit">;
 def err_module_interface_implementation_mismatch : Error<
   "missing 'export' specifier in module declaration while "
   "building module interface">;
@@ -9217,6 +9217,9 @@ def err_module_redeclaration : Error<
 def note_prev_module_declaration : Note<"previous module declaration is here">;
 def err_module_declaration_missing : Error<
   "missing 'export module' declaration in module interface unit">;
+def err_module_declaration_missing_after_global_module_introducer : Error<
+  "missing 'module' declaration at end of global module fragment "
+  "introduced here">;
 def err_module_private_specialization : Error<
   "%select{template|partial|member}0 specialization cannot be "
   "declared __module_private__">;
@@ -9254,7 +9257,12 @@ def err_module_self_import : Error<
 def err_module_import_in_implementation : Error<
   "@import of module '%0' in implementation of '%1'; use #import">;
 
-// C++ Modules TS
+// C++ Modules
+def err_module_decl_not_at_start : Error<
+  "module declaration must occur at the start of the translation unit">;
+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_export : Error<
   "export declaration appears within another export declaration">;
 def err_export_not_in_module_interface : Error<
index db06df1ab8b218bd04eefa5e66283ee644a30571..293fbafdab0dd75caa0761909e5309912bbdb8c3 100644 (file)
@@ -2172,6 +2172,7 @@ private:
       None,
       ModuleBegin,
       ModuleImport,
+      SkippedModuleImport,
     } Kind;
     Module *ModuleForHeader = nullptr;
 
index c77011d759c355f4bde3553e12cc4856e2981918..e831e226d4f34a30eae5bc97bae3c74888de11a3 100644 (file)
@@ -150,11 +150,15 @@ class Parser : public CodeCompletionHandler {
   IdentifierInfo *Ident_language, *Ident_defined_in,
       *Ident_generated_declaration;
 
-  /// C++0x contextual keywords.
+  /// C++11 contextual keywords.
   mutable IdentifierInfo *Ident_final;
   mutable IdentifierInfo *Ident_GNU_final;
   mutable IdentifierInfo *Ident_override;
 
+  // C++2a contextual keywords.
+  mutable IdentifierInfo *Ident_import;
+  mutable IdentifierInfo *Ident_module;
+
   // C++ type trait keywords that can be reverted to identifiers and still be
   // used as type traits.
   llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind> RevertibleTypeTraits;
@@ -424,7 +428,7 @@ public:
 
   /// ParseTopLevelDecl - Parse one top-level declaration. Returns true if
   /// the EOF was encountered.
-  bool ParseTopLevelDecl(DeclGroupPtrTy &Result);
+  bool ParseTopLevelDecl(DeclGroupPtrTy &Result, bool IsFirstDecl = false);
   bool ParseTopLevelDecl() {
     DeclGroupPtrTy Result;
     return ParseTopLevelDecl(Result);
@@ -2996,7 +3000,7 @@ private:
 
   //===--------------------------------------------------------------------===//
   // Modules
-  DeclGroupPtrTy ParseModuleDecl();
+  DeclGroupPtrTy ParseModuleDecl(bool IsFirstDecl);
   Decl *ParseModuleImport(SourceLocation AtLoc);
   bool parseMisplacedModuleImport();
   bool tryParseMisplacedModuleImport() {
index 41f331ca789dbb74ca3ac4faa2723dc660aab782..b09a4eb93d8a7f7fce6673552c2c322678ed2e9a 100644 (file)
@@ -1624,8 +1624,10 @@ private:
                                TypeDiagnoser *Diagnoser);
 
   struct ModuleScope {
+    SourceLocation BeginLoc;
     clang::Module *Module = nullptr;
     bool ModuleInterface = false;
+    bool ImplicitGlobalModuleFragment = false;
     VisibleModuleSet OuterVisibleModules;
   };
   /// The modules we're currently parsing.
@@ -2155,24 +2157,43 @@ public:
   enum class ModuleDeclKind {
     Interface,      ///< 'export module X;'
     Implementation, ///< 'module X;'
-    Partition,      ///< 'module partition X;'
   };
 
   /// The parser has processed a module-declaration that begins the definition
   /// of a module interface or implementation.
   DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
                                  SourceLocation ModuleLoc, ModuleDeclKind MDK,
-                                 ModuleIdPath Path);
+                                 ModuleIdPath Path, bool IsFirstDecl);
+
+  /// The parser has processed a global-module-fragment declaration that begins
+  /// the definition of the global module fragment of the current module unit.
+  /// \param ModuleLoc The location of the 'module' keyword.
+  DeclGroupPtrTy ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc);
+
+  /// The parser has processed a private-module-fragment declaration that begins
+  /// the definition of the private module fragment of the current module unit.
+  /// \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();
+  }
 
   /// The parser has processed a module import declaration.
   ///
-  /// \param AtLoc The location of the '@' symbol, if any.
-  ///
+  /// \param StartLoc The location of the first token in the declaration. This
+  ///        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.
-  DeclResult ActOnModuleImport(SourceLocation AtLoc, SourceLocation ImportLoc,
-                               ModuleIdPath Path);
+  DeclResult ActOnModuleImport(SourceLocation StartLoc,
+                               SourceLocation ExportLoc,
+                               SourceLocation ImportLoc, ModuleIdPath Path);
+  DeclResult ActOnModuleImport(SourceLocation StartLoc,
+                               SourceLocation ExportLoc,
+                               SourceLocation ImportLoc, Module *M,
+                               ModuleIdPath Path = {});
 
   /// The parser has processed a module import translated from a
   /// #include or similar preprocessing directive.
index c4adec83fb10d1a8f96a4a72f1f130ff67b784d2..b3778c1d6f8241bc529a9b1f78b5fed663b9aeff 100644 (file)
@@ -1648,6 +1648,7 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
                                             EndLoc, LookupFrom, LookupFromFile);
   switch (Action.Kind) {
   case ImportAction::None:
+  case ImportAction::SkippedModuleImport:
     break;
   case ImportAction::ModuleBegin:
     EnterAnnotationToken(SourceRange(HashLoc, EndLoc),
@@ -2034,6 +2035,8 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
   switch (Action) {
   case Skip:
     // If we don't need to enter the file, stop now.
+    if (Module *M = SuggestedModule.getModule())
+      return {ImportAction::SkippedModuleImport, M};
     return {ImportAction::None};
 
   case IncludeLimitReached:
@@ -2117,6 +2120,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
     return {ImportAction::ModuleBegin, M};
   }
 
+  assert(!IsImportDecl && "failed to diagnose missing module for import decl");
   return {ImportAction::None};
 }
 
index 48d4a62ab7d5368075d5e190bd6ea47fe2d6cc31..ebd4ae07ba4c7351a4f0d1df6c81d4ddbc561975 100644 (file)
@@ -1168,6 +1168,7 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
       LLVM_FALLTHROUGH;
 
     case ImportAction::ModuleImport:
+    case ImportAction::SkippedModuleImport:
       // We chose to import (or textually enter) the file. Convert the
       // header-name token into a header unit annotation token.
       Suffix[0].setKind(tok::annot_header_unit);
index b477e1f8bbc4ec88c09bebae4b5d62290631a158..d1920a9ae7a6ce4c5d3d89dd9976fa7a4e49e3fd 100644 (file)
@@ -460,6 +460,8 @@ void Parser::Initialize() {
   Ident_sealed = nullptr;
   Ident_override = nullptr;
   Ident_GNU_final = nullptr;
+  Ident_import = nullptr;
+  Ident_module = nullptr;
 
   Ident_super = &PP.getIdentifierTable().get("super");
 
@@ -512,6 +514,11 @@ void Parser::Initialize() {
     PP.SetPoisonReason(Ident_AbnormalTermination,diag::err_seh___finally_block);
   }
 
+  if (getLangOpts().CPlusPlusModules) {
+    Ident_import = PP.getIdentifierInfo("import");
+    Ident_module = PP.getIdentifierInfo("module");
+  }
+
   Actions.Initialize();
 
   // Prime the lexer look-ahead.
@@ -525,6 +532,16 @@ void Parser::LateTemplateParserCleanupCallback(void *P) {
   DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(((Parser *)P)->TemplateIds);
 }
 
+/// Parse the first top-level declaration in a translation unit.
+///
+///   translation-unit:
+/// [C]     external-declaration
+/// [C]     translation-unit external-declaration
+/// [C++]   top-level-declaration-seq[opt]
+/// [C++20] global-module-fragment[opt] module-declaration
+///                 top-level-declaration-seq[opt] private-module-fragment[opt]
+///
+/// Note that in C, it is an error if there is no first declaration.
 bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) {
   Actions.ActOnStartOfTranslationUnit();
 
@@ -532,7 +549,7 @@ bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) {
   // declaration. C++ doesn't have this restriction. We also don't want to
   // complain if we have a precompiled header, although technically if the PCH
   // is empty we should still emit the (pedantic) diagnostic.
-  bool NoTopLevelDecls = ParseTopLevelDecl(Result);
+  bool NoTopLevelDecls = ParseTopLevelDecl(Result, true);
   if (NoTopLevelDecls && !Actions.getASTContext().getExternalSource() &&
       !getLangOpts().CPlusPlus)
     Diag(diag::ext_empty_translation_unit);
@@ -542,7 +559,11 @@ bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) {
 
 /// ParseTopLevelDecl - Parse one top-level declaration, return whatever the
 /// action tells us to.  This returns true if the EOF was encountered.
-bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) {
+///
+///   top-level-declaration:
+///           declaration
+/// [C++20]   module-import-declaration
+bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result, bool IsFirstDecl) {
   DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(TemplateIds);
 
   // Skip over the EOF token, flagging end of previous input for incremental
@@ -557,13 +578,46 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) {
     return false;
 
   case tok::kw_export:
-    if (NextToken().isNot(tok::kw_module))
+    switch (NextToken().getKind()) {
+    case tok::kw_module:
+      goto module_decl;
+
+    // Note: no need to handle kw_import here. We only form kw_import under
+    // the Modules TS, and in that case 'export import' is parsed as an
+    // export-declaration containing an import-declaration.
+
+    // Recognize context-sensitive C++20 'export module' and 'export import'
+    // declarations.
+    case tok::identifier: {
+      IdentifierInfo *II = NextToken().getIdentifierInfo();
+      if ((II == Ident_module || II == Ident_import) &&
+          GetLookAheadToken(2).isNot(tok::coloncolon)) {
+        if (II == Ident_module)
+          goto module_decl;
+        else
+          goto import_decl;
+      }
       break;
-    LLVM_FALLTHROUGH;
+    }
+
+    default:
+      break;
+    }
+    break;
+
   case tok::kw_module:
-    Result = ParseModuleDecl();
+  module_decl:
+    Result = ParseModuleDecl(IsFirstDecl);
     return false;
 
+  // tok::kw_import is handled by ParseExternalDeclaration. (Under the Modules
+  // TS, an import can occur within an export block.)
+  import_decl: {
+    Decl *ImportDecl = ParseModuleImport(SourceLocation());
+    Result = Actions.ConvertDeclToDeclGroup(ImportDecl);
+    return false;
+  }
+
   case tok::annot_module_include:
     Actions.ActOnModuleInclude(Tok.getLocation(),
                                reinterpret_cast<Module *>(
@@ -595,6 +649,21 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) {
     //else don't tell Sema that we ended parsing: more input might come.
     return true;
 
+  case tok::identifier:
+    // C++2a [basic.link]p3:
+    //   A token sequence beginning with 'export[opt] module' or
+    //   'export[opt] import' and not immediately followed by '::'
+    //   is never interpreted as the declaration of a top-level-declaration.
+    if ((Tok.getIdentifierInfo() == Ident_module ||
+         Tok.getIdentifierInfo() == Ident_import) &&
+        NextToken().isNot(tok::coloncolon)) {
+      if (Tok.getIdentifierInfo() == Ident_module)
+        goto module_decl;
+      else
+        goto import_decl;
+    }
+    break;
+
   default:
     break;
   }
@@ -2074,38 +2143,83 @@ void Parser::ParseMicrosoftIfExistsExternalDeclaration() {
   Braces.consumeClose();
 }
 
-/// Parse a C++ Modules TS module declaration, which appears at the beginning
-/// of a module interface, module partition, or module implementation file.
+/// Parse a declaration beginning with the 'module' keyword or C++20
+/// context-sensitive keyword (optionally preceded by 'export').
 ///
-///   module-declaration:   [Modules TS + P0273R0 + P0629R0]
-///     'export'[opt] 'module' 'partition'[opt]
-///            module-name attribute-specifier-seq[opt] ';'
+///   module-declaration:   [Modules TS + P0629R0]
+///     'export'[opt] 'module' module-name attribute-specifier-seq[opt] ';'
 ///
-/// Note that 'partition' is a context-sensitive keyword.
-Parser::DeclGroupPtrTy Parser::ParseModuleDecl() {
+///   global-module-fragment:  [C++2a]
+///     'module' ';' top-level-declaration-seq[opt]
+///   module-declaration:      [C++2a]
+///     'export'[opt] 'module' module-name module-partition[opt]
+///            attribute-specifier-seq[opt] ';'
+///   private-module-fragment: [C++2a]
+///     'module' ':' 'private' ';' top-level-declaration-seq[opt]
+Parser::DeclGroupPtrTy Parser::ParseModuleDecl(bool IsFirstDecl) {
   SourceLocation StartLoc = Tok.getLocation();
 
   Sema::ModuleDeclKind MDK = TryConsumeToken(tok::kw_export)
                                  ? Sema::ModuleDeclKind::Interface
                                  : Sema::ModuleDeclKind::Implementation;
 
-  assert(Tok.is(tok::kw_module) && "not a module declaration");
+  assert(
+      (Tok.is(tok::kw_module) ||
+       (Tok.is(tok::identifier) && Tok.getIdentifierInfo() == Ident_module)) &&
+      "not a module declaration");
   SourceLocation ModuleLoc = ConsumeToken();
 
-  if (Tok.is(tok::identifier) && NextToken().is(tok::identifier) &&
-      Tok.getIdentifierInfo()->isStr("partition")) {
-    // If 'partition' is present, this must be a module interface unit.
-    if (MDK != Sema::ModuleDeclKind::Interface)
-      Diag(Tok.getLocation(), diag::err_module_implementation_partition)
-        << FixItHint::CreateInsertion(ModuleLoc, "export ");
-    MDK = Sema::ModuleDeclKind::Partition;
+  // Attributes appear after the module name, not before.
+  if (Tok.is(tok::l_square))
+    CheckProhibitedCXX11Attribute();
+
+  // Parse a global-module-fragment, if present.
+  if (getLangOpts().CPlusPlusModules && Tok.is(tok::semi)) {
+    SourceLocation SemiLoc = ConsumeToken();
+    if (!IsFirstDecl) {
+      Diag(StartLoc, diag::err_global_module_introducer_not_at_start)
+        << SourceRange(StartLoc, SemiLoc);
+      return nullptr;
+    }
+    if (MDK == Sema::ModuleDeclKind::Interface) {
+      Diag(StartLoc, diag::err_module_fragment_exported)
+        << /*global*/0 << FixItHint::CreateRemoval(StartLoc);
+    }
+    return Actions.ActOnGlobalModuleFragmentDecl(ModuleLoc);
+  }
+
+  // Parse a private-module-fragment, if present.
+  if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon) &&
+      NextToken().is(tok::kw_private)) {
+    if (MDK == Sema::ModuleDeclKind::Interface) {
+      Diag(StartLoc, diag::err_module_fragment_exported)
+        << /*private*/1 << FixItHint::CreateRemoval(StartLoc);
+    }
     ConsumeToken();
+    SourceLocation PrivateLoc = ConsumeToken();
+    if (Tok.is(tok::l_square))
+      CheckProhibitedCXX11Attribute();
+    ExpectAndConsumeSemi(diag::err_private_module_fragment_expected_semi);
+    return Actions.ActOnPrivateModuleFragmentDecl(ModuleLoc, PrivateLoc);
   }
 
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
   if (ParseModuleName(ModuleLoc, Path, /*IsImport*/false))
     return nullptr;
 
+  // Parse the optional module-partition.
+  if (Tok.is(tok::colon)) {
+    SourceLocation ColonLoc = ConsumeToken();
+    SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
+    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.
   ParsedAttributesWithRange Attrs(AttrFactory);
   MaybeParseCXX11Attributes(Attrs);
@@ -2113,7 +2227,7 @@ Parser::DeclGroupPtrTy Parser::ParseModuleDecl() {
 
   ExpectAndConsumeSemi(diag::err_module_expected_semi);
 
-  return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path);
+  return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, IsFirstDecl);
 }
 
 /// Parse a module import declaration. This is essentially the same for
@@ -2124,17 +2238,50 @@ Parser::DeclGroupPtrTy Parser::ParseModuleDecl() {
 ///           '@' 'import' module-name ';'
 /// [ModTS] module-import-declaration:
 ///           'import' module-name attribute-specifier-seq[opt] ';'
+/// [C++2a] module-import-declaration:
+///           'export'[opt] 'import' module-name
+///                   attribute-specifier-seq[opt] ';'
+///           'export'[opt] 'import' module-partition
+///                   attribute-specifier-seq[opt] ';'
+///           'export'[opt] 'import' header-name
+///                   attribute-specifier-seq[opt] ';'
 Decl *Parser::ParseModuleImport(SourceLocation AtLoc) {
-  assert((AtLoc.isInvalid() ? Tok.is(tok::kw_import)
+  SourceLocation StartLoc = AtLoc.isInvalid() ? Tok.getLocation() : AtLoc;
+
+  SourceLocation ExportLoc;
+  TryConsumeToken(tok::kw_export, ExportLoc);
+
+  assert((AtLoc.isInvalid() ? Tok.isOneOf(tok::kw_import, tok::identifier)
                             : Tok.isObjCAtKeyword(tok::objc_import)) &&
          "Improper start to module import");
   bool IsObjCAtImport = Tok.isObjCAtKeyword(tok::objc_import);
   SourceLocation ImportLoc = ConsumeToken();
-  SourceLocation StartLoc = AtLoc.isInvalid() ? ImportLoc : AtLoc;
 
   SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
-  if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
+  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
+    // been diagnosed.
+    ConsumeToken();
+  } else if (Tok.is(tok::annot_header_unit)) {
+    // 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)) {
+    SourceLocation ColonLoc = ConsumeToken();
+    if (ParseModuleName(ImportLoc, Path, /*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))
+      return nullptr;
+  }
 
   ParsedAttributesWithRange Attrs(AttrFactory);
   MaybeParseCXX11Attributes(Attrs);
@@ -2147,7 +2294,12 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc) {
     return nullptr;
   }
 
-  DeclResult Import = Actions.ActOnModuleImport(StartLoc, ImportLoc, Path);
+  DeclResult Import;
+  if (HeaderUnit)
+    Import =
+        Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit);
+  else if (!Path.empty())
+    Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path);
   ExpectAndConsumeSemi(diag::err_module_expected_semi);
   if (Import.isInvalid())
     return nullptr;
index bc4fdfbe58e9cc0c57bfcf9f934fe1acc5d8aa3c..befe6ac76303720d72381177f8562c7f5fa9e6dd 100644 (file)
@@ -849,24 +849,11 @@ void Sema::ActOnStartOfTranslationUnit() {
   if (getLangOpts().ModulesTS &&
       (getLangOpts().getCompilingModule() == LangOptions::CMK_ModuleInterface ||
        getLangOpts().getCompilingModule() == LangOptions::CMK_None)) {
+    // We start in an implied global module fragment.
     SourceLocation StartOfTU =
         SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID());
-
-    // 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(StartOfTU);
-    assert(GlobalModule && "module creation should not fail");
-
-    // Enter the scope of the global module.
-    ModuleScopes.push_back({});
-    ModuleScopes.back().Module = GlobalModule;
-    VisibleModules.setVisible(GlobalModule, StartOfTU);
-
-    // All declarations created from now on are owned by the global module.
-    auto *TU = Context.getTranslationUnitDecl();
-    TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Visible);
-    TU->setLocalOwningModule(GlobalModule);
+    ActOnGlobalModuleFragmentDecl(StartOfTU);
+    ModuleScopes.back().ImplicitGlobalModuleFragment = true;
   }
 }
 
@@ -997,13 +984,24 @@ void Sema::ActOnEndOfTranslationUnit() {
     checkUndefinedButUsed(*this);
   }
 
+  // A global-module-fragment is only permitted within a module unit.
+  bool DiagnosedMissingModuleDeclaration = false;
+  if (!ModuleScopes.empty() &&
+      ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment &&
+      !ModuleScopes.back().ImplicitGlobalModuleFragment) {
+    Diag(ModuleScopes.back().BeginLoc,
+         diag::err_module_declaration_missing_after_global_module_introducer);
+    DiagnosedMissingModuleDeclaration = true;
+  }
+
   if (TUKind == TU_Module) {
     // If we are building a module interface unit, we need to have seen the
     // module declaration by now.
     if (getLangOpts().getCompilingModule() ==
             LangOptions::CMK_ModuleInterface &&
         (ModuleScopes.empty() ||
-         ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit)) {
+         ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit) &&
+        !DiagnosedMissingModuleDeclaration) {
       // FIXME: Make a better guess as to where to put the module declaration.
       Diag(getSourceManager().getLocForStartOfFile(
                getSourceManager().getMainFileID()),
index fa29e8c904fc7b394018e776313135e7abe00aa2..cb1a5b684a5f4dc50fd223e4589fac90794d7d6a 100644 (file)
@@ -17016,12 +17016,44 @@ static void checkModuleImportContext(Sema &S, Module *M,
   }
 }
 
-Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
-                                           SourceLocation ModuleLoc,
-                                           ModuleDeclKind MDK,
-                                           ModuleIdPath Path) {
-  assert(getLangOpts().ModulesTS &&
-         "should only have module decl in modules TS");
+Sema::DeclGroupPtrTy
+Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
+  if (!ModuleScopes.empty() &&
+      ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment) {
+    // Under -std=c++2a -fmodules-ts, we can find an explicit 'module;' after
+    // already implicitly entering the global module fragment. That's OK.
+    assert(getLangOpts().CPlusPlusModules && getLangOpts().ModulesTS &&
+           "unexpectedly encountered multiple global module fragment decls");
+    ModuleScopes.back().BeginLoc = ModuleLoc;
+    return nullptr;
+  }
+
+  // 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);
+  assert(GlobalModule && "module creation should not fail");
+
+  // Enter the scope of the global module.
+  ModuleScopes.push_back({});
+  ModuleScopes.back().BeginLoc = ModuleLoc;
+  ModuleScopes.back().Module = GlobalModule;
+  VisibleModules.setVisible(GlobalModule, ModuleLoc);
+
+  // All declarations created from now on are owned by the global module.
+  auto *TU = Context.getTranslationUnitDecl();
+  TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Visible);
+  TU->setLocalOwningModule(GlobalModule);
+
+  // FIXME: Consider creating an explicit representation of this declaration.
+  return nullptr;
+}
+
+Sema::DeclGroupPtrTy
+Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
+                      ModuleDeclKind MDK, ModuleIdPath Path, bool IsFirstDecl) {
+  assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) &&
+         "should only have module decl in Modules TS or C++20");
 
   // A module implementation unit requires that we are not compiling a module
   // of any kind. A module interface unit requires that we are not compiling a
@@ -17051,19 +17083,40 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
     return nullptr;
   }
 
-  assert(ModuleScopes.size() == 1 && "expected to be at global module scope");
+  assert(ModuleScopes.size() <= 1 && "expected to be at global module scope");
 
   // FIXME: Most of this work should be done by the preprocessor rather than
   // here, in order to support macro import.
 
   // Only one module-declaration is permitted per source file.
-  if (ModuleScopes.back().Module->Kind == Module::ModuleInterfaceUnit) {
+  if (!ModuleScopes.empty() &&
+      ModuleScopes.back().Module->Kind == Module::ModuleInterfaceUnit) {
     Diag(ModuleLoc, diag::err_module_redeclaration);
     Diag(VisibleModules.getImportLoc(ModuleScopes.back().Module),
          diag::note_prev_module_declaration);
     return nullptr;
   }
 
+  // Find the global module fragment we're adopting into this module, if any.
+  Module *GlobalModuleFragment = nullptr;
+  if (!ModuleScopes.empty() &&
+      ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment)
+    GlobalModuleFragment = ModuleScopes.back().Module;
+
+  // In C++20, the module-declaration must be the first declaration if there
+  // is no global module fragment.
+  if (getLangOpts().CPlusPlusModules && !IsFirstDecl && !GlobalModuleFragment) {
+    Diag(ModuleLoc, diag::err_module_decl_not_at_start);
+    SourceLocation BeginLoc =
+        ModuleScopes.empty()
+            ? SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID())
+            : ModuleScopes.back().BeginLoc;
+    if (BeginLoc.isValid()) {
+      Diag(BeginLoc, diag::note_global_module_introducer_missing)
+          << FixItHint::CreateInsertion(BeginLoc, "module;\n");
+    }
+  }
+
   // 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.
@@ -17105,15 +17158,11 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
 
     // Create a Module for the module that we're defining.
     Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
-                                           ModuleScopes.front().Module);
+                                           GlobalModuleFragment);
     assert(Mod && "module creation should not fail");
     break;
   }
 
-  case ModuleDeclKind::Partition:
-    // FIXME: Check we are in a submodule of the named module.
-    return nullptr;
-
   case ModuleDeclKind::Implementation:
     std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc(
         PP.getIdentifierInfo(ModuleName), Path[0].second);
@@ -17124,12 +17173,19 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
       Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName;
       // Create an empty module interface unit for error recovery.
       Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
-                                             ModuleScopes.front().Module);
+                                             GlobalModuleFragment);
     }
     break;
   }
 
-  // Switch from the global module to the named module.
+  if (!GlobalModuleFragment) {
+    ModuleScopes.push_back({});
+    if (getLangOpts().ModulesLocalVisibility)
+      ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules);
+  }
+
+  // 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;
   VisibleModules.setVisible(Mod, ModuleLoc);
@@ -17146,6 +17202,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
 }
 
 DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
+                                   SourceLocation ExportLoc,
                                    SourceLocation ImportLoc,
                                    ModuleIdPath Path) {
   // Flatten the module path for a Modules TS module name.
@@ -17167,6 +17224,13 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
   if (!Mod)
     return true;
 
+  return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path);
+}
+
+DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
+                                   SourceLocation ExportLoc,
+                                   SourceLocation ImportLoc,
+                                   Module *Mod, ModuleIdPath Path) {
   VisibleModules.setVisible(Mod, ImportLoc);
 
   checkModuleImportContext(*this, Mod, ImportLoc, CurContext);
@@ -17176,12 +17240,15 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
   // silently ignoring the import.
   // Import-from-implementation is valid in the Modules TS. FIXME: Should we
   // warn on a redundant import of the current module?
+  // FIXME: Import of a module from an implementation partition of the same
+  // module is permitted.
   if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule &&
-      (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS))
+      (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS)) {
     Diag(ImportLoc, getLangOpts().isCompilingModule()
                         ? diag::err_module_self_import
                         : diag::err_module_import_in_implementation)
         << Mod->getFullModuleName() << getLangOpts().CurrentModule;
+  }
 
   SmallVector<SourceLocation, 2> IdentifierLocs;
   Module *ModCheck = Mod;
@@ -17195,16 +17262,30 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
     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) {
+      IdentifierLocs.push_back(SourceLocation());
+    }
+  }
+
   ImportDecl *Import = ImportDecl::Create(Context, CurContext, StartLoc,
                                           Mod, IdentifierLocs);
+  CurContext->addDecl(Import);
+
+  // Sequence initialization of the imported module before that of the current
+  // module, if any.
   if (!ModuleScopes.empty())
     Context.addModuleInitializer(ModuleScopes.back().Module, Import);
-  CurContext->addDecl(Import);
 
   // Re-export the module if needed.
-  if (Import->isExported() &&
-      !ModuleScopes.empty() && ModuleScopes.back().ModuleInterface)
-    getCurrentModule()->Exports.emplace_back(Mod, false);
+  if (!ModuleScopes.empty() && ModuleScopes.back().ModuleInterface) {
+    if (ExportLoc.isValid() || Import->isExported())
+      getCurrentModule()->Exports.emplace_back(Mod, false);
+  } else if (ExportLoc.isValid()) {
+    Diag(ExportLoc, diag::err_export_not_in_module_interface);
+  }
 
   return Import;
 }
diff --git a/test/CXX/basic/basic.link/p1.cpp b/test/CXX/basic/basic.link/p1.cpp
new file mode 100644 (file)
index 0000000..b94e570
--- /dev/null
@@ -0,0 +1,37 @@
+// 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 -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
+#ifdef EXPORT_FRAGS
+export // expected-error {{global module fragment cannot be exported}}
+#endif
+module; // #glob-frag
+#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
+
+// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
+// expected-note@#a1 {{previous decl}}
+#endif
+
+int a; // #a2
+extern int b;
+
+module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
+
+#ifdef EXPORT_FRAGS
+export // expected-error {{private module fragment cannot be exported}}
+#endif
+module :private;
+
+int b; // ok
diff --git a/test/CXX/basic/basic.link/p3.cpp b/test/CXX/basic/basic.link/p3.cpp
new file mode 100644 (file)
index 0000000..23f39d1
--- /dev/null
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+// RUN: %clang_cc1 -std=c++2a -verify %s -DIMPORT_ERROR=1
+// RUN: %clang_cc1 -std=c++2a -verify %s -DIMPORT_ERROR=2
+
+module;
+
+#if IMPORT_ERROR != 2
+struct import { struct inner {}; };
+#endif
+struct module { struct inner {}; };
+
+constexpr int n = 123;
+
+export module m; // #1
+
+// Import errors are fatal, so we test them in isolation.
+#if IMPORT_ERROR == 1
+import x = {}; // expected-error {{module 'x' not found}}
+
+#elif IMPORT_ERROR == 2
+struct X;
+template<int> struct import;
+template<> struct import<n> {
+  static X y;
+};
+
+// This is not valid because the 'import <n>' is a pp-import, even though it
+// grammatically can't possibly be an import declaration.
+struct X {} import<n>::y; // expected-error {{'n' file not found}}
+
+#else
+module y = {}; // expected-error {{multiple module declarations}} expected-error 2{{}}
+// expected-note@#1 {{previous module declaration}}
+
+::import x = {};
+::module y = {};
+
+import::inner xi = {};
+module::inner yi = {};
+
+namespace N {
+  module a;
+  import b;
+}
+
+extern "C++" module cxxm;
+extern "C++" import cxxi;
+
+template<typename T> module module_var_template;
+
+// This is a variable named 'import' that shadows the type 'import' above.
+struct X {} import;
+#endif
diff --git a/test/CXX/module/module.unit/p3.cpp b/test/CXX/module/module.unit/p3.cpp
new file mode 100644 (file)
index 0000000..2e08f4a
--- /dev/null
@@ -0,0 +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}}
index feb0afdda07b1429149d1a67d1fcadd0f62d18b8..7615536b81fc488bce307d8e20d4807cbdaba8c7 100644 (file)
 //
 // Module implementation for unknown and known module. (The former is ill-formed.)
 // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
-// RUN:            -DTEST=1 -DEXPORT= -DPARTITION= -DMODULE_NAME=z
+// RUN:            -DTEST=1 -DEXPORT= -DMODULE_NAME=z
 // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
-// RUN:            -DTEST=2 -DEXPORT= -DPARTITION= -DMODULE_NAME=x
+// RUN:            -DTEST=2 -DEXPORT= -DMODULE_NAME=x
 //
 // Module interface for unknown and known module. (The latter is ill-formed due to
 // redefinition.)
 // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
-// RUN:            -DTEST=3 -DEXPORT=export -DPARTITION= -DMODULE_NAME=z
+// RUN:            -DTEST=3 -DEXPORT=export -DMODULE_NAME=z
 // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
-// RUN:            -DTEST=4 -DEXPORT=export -DPARTITION= -DMODULE_NAME=x
-//
-// Defining a module partition.
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
-// RUN:            -DTEST=5 -DEXPORT=export -DPARTITION=partition -DMODULE_NAME=z
-// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
-// RUN:            -DTEST=6 -DEXPORT= -DPARTITION=partition -DMODULE_NAME=z
+// RUN:            -DTEST=4 -DEXPORT=export -DMODULE_NAME=x
 //
 // Miscellaneous syntax.
 // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
-// RUN:            -DTEST=7 -DEXPORT= -DPARTITION=elderberry -DMODULE_NAME=z
+// RUN:            -DTEST=7 -DEXPORT=export -DMODULE_NAME='z elderberry'
 // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
-// RUN:            -DTEST=8 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[]]'
+// RUN:            -DTEST=8 -DEXPORT=export -DMODULE_NAME='z [[]]'
 // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
-// RUN:            -DTEST=9 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[fancy]]'
+// RUN:            -DTEST=9 -DEXPORT=export -DMODULE_NAME='z [[fancy]]'
 // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
-// RUN:            -DTEST=10 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[maybe_unused]]'
+// RUN:            -DTEST=10 -DEXPORT=export -DMODULE_NAME='z [[maybe_unused]]'
 
-EXPORT module PARTITION MODULE_NAME;
+EXPORT module MODULE_NAME;
 #if TEST == 4
 // expected-error@-2 {{redefinition of module 'x'}}
 // expected-note-re@module-declaration.cpp:* {{loaded from '{{.*[/\\]}}x.pcm'}}
-#elif TEST == 6
-// expected-error@-5 {{module partition must be declared 'export'}}
 #elif TEST == 7
-// expected-error@-7 {{expected ';'}} expected-error@-7 {{requires a type specifier}} expected-error@-7 {{definition of module 'elderberry' is not available}}
+// expected-error@-5 {{expected ';'}} expected-error@-5 {{requires a type specifier}}
 #elif TEST == 9
-// expected-warning@-9 {{unknown attribute 'fancy' ignored}}
+// expected-warning@-7 {{unknown attribute 'fancy' ignored}}
 #elif TEST == 10
-// expected-error-re@-11 {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
+// expected-error-re@-9 {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
 #elif TEST == 1
-// expected-error@-13 {{definition of module 'z' is not available}}
+// expected-error@-11 {{definition of module 'z' is not available}}
 #else
 // expected-no-diagnostics
 #endif