Add #pragma clang module begin/end pragmas and generate them when preprocessing a...
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 4 May 2017 00:29:54 +0000 (00:29 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 4 May 2017 00:29:54 +0000 (00:29 +0000)
These pragmas are intended to simulate the effect of entering or leaving a file
with an associated module. This is not completely implemented yet: declarations
between the pragmas will not be attributed to the correct module, but macro
visibility is already functional.

Modules named by #pragma clang module begin must already be known to clang (in
some module map that's either loaded or on the search path).

llvm-svn: 302098

13 files changed:
clang/include/clang/Basic/DiagnosticLexKinds.td
clang/include/clang/Lex/Preprocessor.h
clang/lib/Frontend/PrintPreprocessedOutput.cpp
clang/lib/Frontend/Rewrite/InclusionRewriter.cpp
clang/lib/Lex/PPDirectives.cpp
clang/lib/Lex/PPLexerChange.cpp
clang/lib/Lex/PPMacroExpansion.cpp
clang/lib/Lex/Pragma.cpp
clang/lib/Lex/Preprocessor.cpp
clang/test/Modules/Inputs/preprocess/file2.h [new file with mode: 0644]
clang/test/Modules/Inputs/preprocess/module.modulemap
clang/test/Modules/preprocess-module.cpp
clang/test/Preprocessor/pragma_module.c

index cd284e9..77db899 100644 (file)
@@ -475,8 +475,6 @@ def warn_pragma_pop_macro_no_push : Warning<
 def warn_pragma_message : Warning<"%0">,
    InGroup<PoundPragmaMessage>, DefaultWarnNoWerror;
 def err_pragma_message : Error<"%0">;
-def err_pragma_module_import_expected_module_name : Error<
-  "expected %select{identifier in|'.' or end of directive after}0 module name">;
 def warn_pragma_ignored : Warning<"unknown pragma ignored">,
    InGroup<UnknownPragmas>, DefaultIgnore;
 def ext_stdc_pragma_ignored : ExtWarn<"unknown pragma in STDC namespace">,
@@ -511,6 +509,22 @@ def warn_pragma_debug_unexpected_command : Warning<
   "unexpected debug command '%0'">, InGroup<IgnoredPragmas>;
 def warn_pragma_debug_missing_argument : Warning<
   "missing argument to debug command '%0'">, InGroup<IgnoredPragmas>;
+// #pragma module
+def err_pp_expected_module_name : Error<
+  "expected %select{identifier after '.' in |}0module name">;
+def err_pp_module_begin_wrong_module : Error<
+  "must specify '-fmodule-name=%0' to enter %select{|submodule of }1"
+  "this module%select{ (current module is %3)|}2">;
+def err_pp_module_begin_no_module_map : Error<
+  "no module map available for module %0">;
+def err_pp_module_begin_no_submodule : Error<
+  "submodule %0.%1 not declared in module map">;
+def err_pp_module_begin_without_module_end : Error<
+  "no matching '#pragma clang module end' for this "
+  "'#pragma clang module begin'">;
+def err_pp_module_end_without_module_begin : Error<
+  "no matching '#pragma clang module begin' for this "
+  "'#pragma clang module end'">;
 
 def err_defined_macro_name : Error<"'defined' cannot be used as a macro name">;
 def err_paste_at_start : Error<
index b1a7325..0e3f563 100644 (file)
@@ -324,7 +324,7 @@ class Preprocessor {
 
   /// \brief If the current lexer is for a submodule that is being built, this
   /// is that submodule.
-  Module *CurSubmodule;
+  Module *CurLexerSubmodule;
 
   /// \brief Keeps track of the stack of files currently
   /// \#included, and macros currently being expanded from, not counting
@@ -507,16 +507,19 @@ class Preprocessor {
 
   /// \brief Information about a submodule that we're currently building.
   struct BuildingSubmoduleInfo {
-    BuildingSubmoduleInfo(Module *M, SourceLocation ImportLoc,
+    BuildingSubmoduleInfo(Module *M, SourceLocation ImportLoc, bool IsPragma,
                           SubmoduleState *OuterSubmoduleState,
                           unsigned OuterPendingModuleMacroNames)
-        : M(M), ImportLoc(ImportLoc), OuterSubmoduleState(OuterSubmoduleState),
+        : M(M), ImportLoc(ImportLoc), IsPragma(IsPragma),
+          OuterSubmoduleState(OuterSubmoduleState),
           OuterPendingModuleMacroNames(OuterPendingModuleMacroNames) {}
 
     /// The module that we are building.
     Module *M;
     /// The location at which the module was included.
     SourceLocation ImportLoc;
+    /// Whether we entered this submodule via a pragma.
+    bool IsPragma;
     /// The previous SubmoduleState.
     SubmoduleState *OuterSubmoduleState;
     /// The number of pending module macro names when we started building this.
@@ -773,8 +776,9 @@ public:
   /// expansions going on at the time.
   PreprocessorLexer *getCurrentFileLexer() const;
 
-  /// \brief Return the submodule owning the file being lexed.
-  Module *getCurrentSubmodule() const { return CurSubmodule; }
+  /// \brief Return the submodule owning the file being lexed. This may not be
+  /// the current module if we have changed modules since entering the file.
+  Module *getCurrentLexerSubmodule() const { return CurLexerSubmodule; }
 
   /// \brief Returns the FileID for the preprocessor predefines.
   FileID getPredefinesFileID() const { return PredefinesFileID; }
@@ -1726,13 +1730,16 @@ public:
   bool CheckMacroName(Token &MacroNameTok, MacroUse isDefineUndef,
                       bool *ShadowFlag = nullptr);
 
-private:
+  void EnterSubmodule(Module *M, SourceLocation ImportLoc, bool ForPragma);
+  Module *LeaveSubmodule(bool ForPragma);
 
+private:
   void PushIncludeMacroStack() {
     assert(CurLexerKind != CLK_CachingLexer && "cannot push a caching lexer");
-    IncludeMacroStack.emplace_back(
-        CurLexerKind, CurSubmodule, std::move(CurLexer), std::move(CurPTHLexer),
-        CurPPLexer, std::move(CurTokenLexer), CurDirLookup);
+    IncludeMacroStack.emplace_back(CurLexerKind, CurLexerSubmodule,
+                                   std::move(CurLexer), std::move(CurPTHLexer),
+                                   CurPPLexer, std::move(CurTokenLexer),
+                                   CurDirLookup);
     CurPPLexer = nullptr;
   }
 
@@ -1742,16 +1749,13 @@ private:
     CurPPLexer = IncludeMacroStack.back().ThePPLexer;
     CurTokenLexer = std::move(IncludeMacroStack.back().TheTokenLexer);
     CurDirLookup  = IncludeMacroStack.back().TheDirLookup;
-    CurSubmodule = IncludeMacroStack.back().TheSubmodule;
+    CurLexerSubmodule = IncludeMacroStack.back().TheSubmodule;
     CurLexerKind = IncludeMacroStack.back().CurLexerKind;
     IncludeMacroStack.pop_back();
   }
 
   void PropagateLineStartLeadingSpaceInfo(Token &Result);
 
-  void EnterSubmodule(Module *M, SourceLocation ImportLoc);
-  void LeaveSubmodule();
-
   /// Determine whether we need to create module macros for #defines in the
   /// current context.
   bool needModuleMacros() const;
@@ -1967,7 +1971,6 @@ public:
   void HandlePragmaPoison();
   void HandlePragmaSystemHeader(Token &SysHeaderTok);
   void HandlePragmaDependency(Token &DependencyTok);
-  void HandlePragmaModuleImport(Token &Tok);
   void HandlePragmaPushMacro(Token &Tok);
   void HandlePragmaPopMacro(Token &Tok);
   void HandlePragmaIncludeAlias(Token &Tok);
index ffedf3c..832eaf2 100644 (file)
@@ -174,6 +174,9 @@ public:
   void MacroUndefined(const Token &MacroNameTok,
                       const MacroDefinition &MD,
                       const MacroDirective *Undef) override;
+
+  void BeginModule(const Module *M);
+  void EndModule(const Module *M);
 };
 }  // end anonymous namespace
 
@@ -372,6 +375,20 @@ void PrintPPOutputPPCallbacks::InclusionDirective(SourceLocation HashLoc,
   }
 }
 
+/// Handle entering the scope of a module during a module compilation.
+void PrintPPOutputPPCallbacks::BeginModule(const Module *M) {
+  startNewLineIfNeeded();
+  OS << "#pragma clang module begin " << M->getFullModuleName();
+  setEmittedDirectiveOnThisLine();
+}
+
+/// Handle leaving the scope of a module during a module compilation.
+void PrintPPOutputPPCallbacks::EndModule(const Module *M) {
+  startNewLineIfNeeded();
+  OS << "#pragma clang module end /*" << M->getFullModuleName() << "*/";
+  setEmittedDirectiveOnThisLine();
+}
+
 /// Ident - Handle #ident directives when read by the preprocessor.
 ///
 void PrintPPOutputPPCallbacks::Ident(SourceLocation Loc, StringRef S) {
@@ -685,13 +702,27 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok,
       // -traditional-cpp the lexer keeps /all/ whitespace, including comments.
       SourceLocation StartLoc = Tok.getLocation();
       Callbacks->MoveToLine(StartLoc.getLocWithOffset(Tok.getLength()));
-    } else if (Tok.is(tok::annot_module_include) ||
-               Tok.is(tok::annot_module_begin) ||
-               Tok.is(tok::annot_module_end)) {
+    } else if (Tok.is(tok::annot_module_include)) {
       // PrintPPOutputPPCallbacks::InclusionDirective handles producing
       // appropriate output here. Ignore this token entirely.
       PP.Lex(Tok);
       continue;
+    } else if (Tok.is(tok::annot_module_begin)) {
+      // FIXME: We retrieve this token after the FileChanged callback, and
+      // retrieve the module_end token before the FileChanged callback, so
+      // we render this within the file and render the module end outside the
+      // file, but this is backwards from the token locations: the module_begin
+      // token is at the include location (outside the file) and the module_end
+      // token is at the EOF location (within the file).
+      Callbacks->BeginModule(
+          reinterpret_cast<Module *>(Tok.getAnnotationValue()));
+      PP.Lex(Tok);
+      continue;
+    } else if (Tok.is(tok::annot_module_end)) {
+      Callbacks->EndModule(
+          reinterpret_cast<Module *>(Tok.getAnnotationValue()));
+      PP.Lex(Tok);
+      continue;
     } else if (IdentifierInfo *II = Tok.getIdentifierInfo()) {
       OS << II->getName();
     } else if (Tok.isLiteral() && !Tok.needsCleaning() &&
index ee61f76..d45cbc0 100644 (file)
@@ -46,6 +46,8 @@ class InclusionRewriter : public PPCallbacks {
   std::map<unsigned, IncludedFile> FileIncludes;
   /// Tracks where inclusions that import modules are found.
   std::map<unsigned, const Module *> ModuleIncludes;
+  /// Tracks where inclusions that enter modules (in a module build) are found.
+  std::map<unsigned, const Module *> ModuleEntryIncludes;
   /// Used transitively for building up the FileIncludes mapping over the
   /// various \c PPCallbacks callbacks.
   SourceLocation LastInclusionLocation;
@@ -57,6 +59,11 @@ public:
     PredefinesBuffer = Buf;
   }
   void detectMainFileEOL();
+  void handleModuleBegin(Token &Tok) {
+    assert(Tok.getKind() == tok::annot_module_begin);
+    ModuleEntryIncludes.insert({Tok.getLocation().getRawEncoding(),
+                                (Module *)Tok.getAnnotationValue()});
+  }
 private:
   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
                    SrcMgr::CharacteristicKind FileType,
@@ -84,6 +91,7 @@ private:
                         bool &FileExists);
   const IncludedFile *FindIncludeAtLocation(SourceLocation Loc) const;
   const Module *FindModuleAtLocation(SourceLocation Loc) const;
+  const Module *FindEnteredModule(SourceLocation Loc) const;
   StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken);
 };
 
@@ -211,6 +219,16 @@ InclusionRewriter::FindModuleAtLocation(SourceLocation Loc) const {
   return nullptr;
 }
 
+/// Simple lookup for a SourceLocation (specifically one denoting the hash in
+/// an inclusion directive) in the map of module entry information.
+const Module *
+InclusionRewriter::FindEnteredModule(SourceLocation Loc) const {
+  const auto I = ModuleEntryIncludes.find(Loc.getRawEncoding());
+  if (I != ModuleEntryIncludes.end())
+    return I->second;
+  return nullptr;
+}
+
 /// Detect the likely line ending style of \p FromFile by examining the first
 /// newline found within it.
 static StringRef DetectEOL(const MemoryBuffer &FromFile) {
@@ -452,8 +470,18 @@ void InclusionRewriter::Process(FileID FileId,
             if (const Module *Mod = FindModuleAtLocation(Loc))
               WriteImplicitModuleImport(Mod);
             else if (const IncludedFile *Inc = FindIncludeAtLocation(Loc)) {
+              const Module *Mod = FindEnteredModule(Loc);
+              if (Mod)
+                OS << "#pragma clang module begin " << Mod->getFullModuleName()
+                   << "\n";
+
               // Include and recursively process the file.
               Process(Inc->Id, Inc->FileType);
+
+              if (Mod)
+                OS << "#pragma clang module end /*" << Mod->getFullModuleName()
+                   << "*/\n";
+
               // Add line marker to indicate we're returning from an included
               // file.
               LineInfoExtra = " 2";
@@ -590,6 +618,8 @@ void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS,
   PP.SetMacroExpansionOnlyInDirectives();
   do {
     PP.Lex(Tok);
+    if (Tok.is(tok::annot_module_begin))
+      Rewrite->handleModuleBegin(Tok);
   } while (Tok.isNot(tok::eof));
   Rewrite->setPredefinesBuffer(SM.getBuffer(PP.getPredefinesFileID()));
   Rewrite->Process(PP.getPredefinesFileID(), SrcMgr::C_User);
index 4826e39..06fee8e 100644 (file)
@@ -2049,12 +2049,12 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
         M->getTopLevelModuleName() == getLangOpts().CurrentModule)
       return;
 
-    assert(!CurSubmodule && "should not have marked this as a module yet");
-    CurSubmodule = M;
+    assert(!CurLexerSubmodule && "should not have marked this as a module yet");
+    CurLexerSubmodule = M;
 
     // Let the macro handling code know that any future macros are within
     // the new submodule.
-    EnterSubmodule(M, HashLoc);
+    EnterSubmodule(M, HashLoc, /*ForPragma*/false);
 
     // Let the parser know that any future declarations are within the new
     // submodule.
@@ -2082,7 +2082,7 @@ void Preprocessor::HandleIncludeNextDirective(SourceLocation HashLoc,
   } else if (isInPrimaryFile()) {
     Lookup = nullptr;
     Diag(IncludeNextTok, diag::pp_include_next_in_primary);
-  } else if (CurSubmodule) {
+  } else if (CurLexerSubmodule) {
     // Start looking up in the directory *after* the one in which the current
     // file would be found, if any.
     assert(CurPPLexer && "#include_next directive in macro?");
index fcc49b3..1938328 100644 (file)
@@ -117,7 +117,7 @@ void Preprocessor::EnterSourceFileWithLexer(Lexer *TheLexer,
   CurLexer.reset(TheLexer);
   CurPPLexer = TheLexer;
   CurDirLookup = CurDir;
-  CurSubmodule = nullptr;
+  CurLexerSubmodule = nullptr;
   if (CurLexerKind != CLK_LexAfterModuleImport)
     CurLexerKind = CLK_Lexer;
 
@@ -142,7 +142,7 @@ void Preprocessor::EnterSourceFileWithPTH(PTHLexer *PL,
   CurDirLookup = CurDir;
   CurPTHLexer.reset(PL);
   CurPPLexer = CurPTHLexer.get();
-  CurSubmodule = nullptr;
+  CurLexerSubmodule = nullptr;
   if (CurLexerKind != CLK_LexAfterModuleImport)
     CurLexerKind = CLK_PTHLexer;
   
@@ -337,6 +337,26 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
   assert(!CurTokenLexer &&
          "Ending a file when currently in a macro!");
 
+  // If we have an unclosed module region from a pragma at the end of a
+  // module, complain and close it now.
+  // FIXME: This is not correct if we are building a module from PTH.
+  const bool LeavingSubmodule = CurLexer && CurLexerSubmodule;
+  if ((LeavingSubmodule || IncludeMacroStack.empty()) &&
+      !BuildingSubmoduleStack.empty() &&
+      BuildingSubmoduleStack.back().IsPragma) {
+    Diag(BuildingSubmoduleStack.back().ImportLoc,
+         diag::err_pp_module_begin_without_module_end);
+    Module *M = LeaveSubmodule(/*ForPragma*/true);
+
+    Result.startToken();
+    const char *EndPos = getCurLexerEndPos();
+    CurLexer->BufferPtr = EndPos;
+    CurLexer->FormTokenWithChars(Result, EndPos, tok::annot_module_end);
+    Result.setAnnotationEndLoc(Result.getLocation());
+    Result.setAnnotationValue(M);
+    return true;
+  }
+
   // See if this file had a controlling macro.
   if (CurPPLexer) {  // Not ending a macro, ignore it.
     if (const IdentifierInfo *ControllingMacro =
@@ -442,18 +462,17 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
     if (Callbacks && !isEndOfMacro && CurPPLexer)
       ExitedFID = CurPPLexer->getFileID();
 
-    bool LeavingSubmodule = CurSubmodule && CurLexer;
     if (LeavingSubmodule) {
+      // We're done with this submodule.
+      Module *M = LeaveSubmodule(/*ForPragma*/false);
+
       // Notify the parser that we've left the module.
       const char *EndPos = getCurLexerEndPos();
       Result.startToken();
       CurLexer->BufferPtr = EndPos;
       CurLexer->FormTokenWithChars(Result, EndPos, tok::annot_module_end);
       Result.setAnnotationEndLoc(Result.getLocation());
-      Result.setAnnotationValue(CurSubmodule);
-
-      // We're done with this submodule.
-      LeaveSubmodule();
+      Result.setAnnotationValue(M);
     }
 
     // We're done with the #included file.
@@ -628,11 +647,13 @@ void Preprocessor::HandleMicrosoftCommentPaste(Token &Tok) {
   assert(!FoundLexer && "Lexer should return EOD before EOF in PP mode");
 }
 
-void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc) {
+void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc,
+                                  bool ForPragma) {
   if (!getLangOpts().ModulesLocalVisibility) {
     // Just track that we entered this submodule.
-    BuildingSubmoduleStack.push_back(BuildingSubmoduleInfo(
-        M, ImportLoc, CurSubmoduleState, PendingModuleMacroNames.size()));
+    BuildingSubmoduleStack.push_back(
+        BuildingSubmoduleInfo(M, ImportLoc, ForPragma, CurSubmoduleState,
+                              PendingModuleMacroNames.size()));
     return;
   }
 
@@ -673,8 +694,9 @@ void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc) {
   }
 
   // Track that we entered this module.
-  BuildingSubmoduleStack.push_back(BuildingSubmoduleInfo(
-      M, ImportLoc, CurSubmoduleState, PendingModuleMacroNames.size()));
+  BuildingSubmoduleStack.push_back(
+      BuildingSubmoduleInfo(M, ImportLoc, ForPragma, CurSubmoduleState,
+                            PendingModuleMacroNames.size()));
 
   // Switch to this submodule as the current submodule.
   CurSubmoduleState = &State;
@@ -697,7 +719,13 @@ bool Preprocessor::needModuleMacros() const {
   return getLangOpts().isCompilingModule();
 }
 
-void Preprocessor::LeaveSubmodule() {
+Module *Preprocessor::LeaveSubmodule(bool ForPragma) {
+  if (BuildingSubmoduleStack.empty() ||
+      BuildingSubmoduleStack.back().IsPragma != ForPragma) {
+    assert(ForPragma && "non-pragma module enter/leave mismatch");
+    return nullptr;
+  }
+
   auto &Info = BuildingSubmoduleStack.back();
 
   Module *LeavingMod = Info.M;
@@ -711,7 +739,7 @@ void Preprocessor::LeaveSubmodule() {
     // of pending names for the surrounding submodule.
     BuildingSubmoduleStack.pop_back();
     makeModuleVisible(LeavingMod, ImportLoc);
-    return;
+    return LeavingMod;
   }
 
   // Create ModuleMacros for any macros defined in this submodule.
@@ -800,4 +828,5 @@ void Preprocessor::LeaveSubmodule() {
 
   // A nested #include makes the included submodule visible.
   makeModuleVisible(LeavingMod, ImportLoc);
+  return LeavingMod;
 }
index 1962239..6c76639 100644 (file)
@@ -1453,7 +1453,7 @@ static bool EvaluateHasIncludeNext(Token &Tok,
   } else if (PP.isInPrimaryFile()) {
     Lookup = nullptr;
     PP.Diag(Tok, diag::pp_include_next_in_primary);
-  } else if (PP.getCurrentSubmodule()) {
+  } else if (PP.getCurrentLexerSubmodule()) {
     // Start looking up in the directory *after* the one in which the current
     // file would be found, if any.
     assert(PP.getCurrentLexer() && "#include_next directive in macro?");
index 576151a..51da2ba 100644 (file)
@@ -534,47 +534,6 @@ void Preprocessor::HandlePragmaDependency(Token &DependencyTok) {
   }
 }
 
-void Preprocessor::HandlePragmaModuleImport(Token &ImportTok) {
-  SourceLocation ImportLoc = ImportTok.getLocation();
-
-  Token Tok;
-
-  llvm::SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 8> ModuleName;
-  while (true) {
-    LexUnexpandedToken(Tok);
-    if (Tok.isNot(tok::identifier)) {
-      Diag(Tok.getLocation(),
-           diag::err_pragma_module_import_expected_module_name) << 0;
-      return;
-    }
-
-    ModuleName.emplace_back(Tok.getIdentifierInfo(), Tok.getLocation());
-
-    LexUnexpandedToken(Tok);
-    assert(Tok.isNot(tok::eof));
-    if (Tok.is(tok::eod))
-      break;
-    if (Tok.isNot(tok::period)) {
-      Diag(Tok.getLocation(),
-           diag::err_pragma_module_import_expected_module_name) << 1;
-      return;
-    }
-  }
-
-  // If we have a non-empty module path, load the named module.
-  Module *Imported =
-      TheModuleLoader.loadModule(ImportLoc, ModuleName, Module::Hidden,
-                                 /*IsIncludeDirective=*/false);
-  if (!Imported)
-    return;
-
-  makeModuleVisible(Imported, ImportLoc);
-  EnterAnnotationToken(SourceRange(ImportLoc, Tok.getLocation()),
-                       tok::annot_module_include, Imported);
-  if (Callbacks)
-    Callbacks->moduleImport(ImportLoc, ModuleName, Imported);
-}
-
 /// ParsePragmaPushOrPopMacro - Handle parsing of pragma push_macro/pop_macro.
 /// Return the IdentifierInfo* associated with the macro to push or pop.
 IdentifierInfo *Preprocessor::ParsePragmaPushOrPopMacro(Token &Tok) {
@@ -1342,6 +1301,26 @@ public:
   }
 };
 
+static bool LexModuleName(
+    Preprocessor &PP, Token &Tok,
+    llvm::SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>>
+        &ModuleName) {
+  while (true) {
+    PP.LexUnexpandedToken(Tok);
+    if (Tok.isNot(tok::identifier)) {
+      PP.Diag(Tok.getLocation(), diag::err_pp_expected_module_name)
+        << ModuleName.empty();
+      return true;
+    }
+
+    ModuleName.emplace_back(Tok.getIdentifierInfo(), Tok.getLocation());
+
+    PP.LexUnexpandedToken(Tok);
+    if (Tok.isNot(tok::period))
+      return false;
+  }
+}
+
 /// Handle the clang \#pragma module import extension. The syntax is:
 /// \code
 ///   #pragma clang module import some.module.name
@@ -1350,8 +1329,108 @@ struct PragmaModuleImportHandler : public PragmaHandler {
   PragmaModuleImportHandler() : PragmaHandler("import") {}
 
   void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
-                    Token &ImportTok) override {
-    PP.HandlePragmaModuleImport(ImportTok);
+                    Token &Tok) override {
+    SourceLocation ImportLoc = Tok.getLocation();
+
+    // Read the module name.
+    llvm::SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 8>
+        ModuleName;
+    if (LexModuleName(PP, Tok, ModuleName))
+      return;
+
+    if (Tok.isNot(tok::eod))
+      PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+
+    // If we have a non-empty module path, load the named module.
+    Module *Imported =
+        PP.getModuleLoader().loadModule(ImportLoc, ModuleName, Module::Hidden,
+                                      /*IsIncludeDirective=*/false);
+    if (!Imported)
+      return;
+
+    PP.makeModuleVisible(Imported, ImportLoc);
+    PP.EnterAnnotationToken(SourceRange(ImportLoc, ModuleName.back().second),
+                            tok::annot_module_include, Imported);
+    if (auto *CB = PP.getPPCallbacks())
+      CB->moduleImport(ImportLoc, ModuleName, Imported);
+  }
+};
+
+/// Handle the clang \#pragma module begin extension. The syntax is:
+/// \code
+///   #pragma clang module begin some.module.name
+///   ...
+///   #pragma clang module end
+/// \endcode
+struct PragmaModuleBeginHandler : public PragmaHandler {
+  PragmaModuleBeginHandler() : PragmaHandler("begin") {}
+
+  void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
+                    Token &Tok) override {
+    SourceLocation BeginLoc = Tok.getLocation();
+
+    // Read the module name.
+    llvm::SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 8>
+        ModuleName;
+    if (LexModuleName(PP, Tok, ModuleName))
+      return;
+
+    if (Tok.isNot(tok::eod))
+      PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+
+    // We can only enter submodules of the current module.
+    StringRef Current = PP.getLangOpts().CurrentModule;
+    if (ModuleName.front().first->getName() != Current) {
+      PP.Diag(ModuleName.front().second, diag::err_pp_module_begin_wrong_module)
+        << ModuleName.front().first << (ModuleName.size() > 1)
+        << Current.empty() << Current;
+      return;
+    }
+
+    // Find the module we're entering. We require that a module map for it
+    // be loaded or implicitly loadable.
+    // FIXME: We could create the submodule here. We'd need to know whether
+    // it's supposed to be explicit, but not much else.
+    Module *M = PP.getHeaderSearchInfo().getModuleMap().findModule(Current);
+    if (!M) {
+      PP.Diag(ModuleName.front().second,
+              diag::err_pp_module_begin_no_module_map) << Current;
+      return;
+    }
+    for (unsigned I = 1; I != ModuleName.size(); ++I) {
+      auto *NewM = M->findSubmodule(ModuleName[I].first->getName());
+      if (!NewM) {
+        PP.Diag(ModuleName[I].second, diag::err_pp_module_begin_no_submodule)
+          << M->getFullModuleName() << ModuleName[I].first;
+        return;
+      }
+      M = NewM;
+    }
+
+    // Enter the scope of the submodule.
+    PP.EnterSubmodule(M, BeginLoc, /*ForPragma*/true);
+    PP.EnterAnnotationToken(SourceRange(BeginLoc, ModuleName.back().second),
+                            tok::annot_module_begin, M);
+  }
+};
+
+/// Handle the clang \#pragma module end extension.
+struct PragmaModuleEndHandler : public PragmaHandler {
+  PragmaModuleEndHandler() : PragmaHandler("end") {}
+
+  void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
+                    Token &Tok) override {
+    SourceLocation Loc = Tok.getLocation();
+
+    PP.LexUnexpandedToken(Tok);
+    if (Tok.isNot(tok::eod))
+      PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
+
+    Module *M = PP.LeaveSubmodule(/*ForPragma*/true);
+    if (M)
+      PP.EnterAnnotationToken(SourceRange(Loc), tok::annot_module_end, M);
+    else
+      PP.Diag(Loc, diag::err_pp_module_end_without_module_begin);
   }
 };
 
@@ -1582,6 +1661,8 @@ void Preprocessor::RegisterBuiltinPragmas() {
   auto *ModuleHandler = new PragmaNamespace("module");
   AddPragmaHandler("clang", ModuleHandler);
   ModuleHandler->AddPragma(new PragmaModuleImportHandler());
+  ModuleHandler->AddPragma(new PragmaModuleBeginHandler());
+  ModuleHandler->AddPragma(new PragmaModuleEndHandler());
 
   AddPragmaHandler("STDC", new PragmaSTDC_FENV_ACCESSHandler());
   AddPragmaHandler("STDC", new PragmaSTDC_CX_LIMITED_RANGEHandler());
index babef5d..e409ab0 100644 (file)
@@ -85,10 +85,10 @@ Preprocessor::Preprocessor(std::shared_ptr<PreprocessorOptions> PPOpts,
       LastTokenWasAt(false), ModuleImportExpectsIdentifier(false),
       CodeCompletionReached(false), CodeCompletionII(nullptr),
       MainFileDir(nullptr), SkipMainFilePreamble(0, true), CurPPLexer(nullptr),
-      CurDirLookup(nullptr), CurLexerKind(CLK_Lexer), CurSubmodule(nullptr),
-      Callbacks(nullptr), CurSubmoduleState(&NullSubmoduleState),
-      MacroArgCache(nullptr), Record(nullptr), MIChainHead(nullptr),
-      DeserialMIChainHead(nullptr) {
+      CurDirLookup(nullptr), CurLexerKind(CLK_Lexer),
+      CurLexerSubmodule(nullptr), Callbacks(nullptr),
+      CurSubmoduleState(&NullSubmoduleState), MacroArgCache(nullptr),
+      Record(nullptr), MIChainHead(nullptr), DeserialMIChainHead(nullptr) {
   OwnsHeaderSearch = OwnsHeaders;
   
   CounterValue = 0; // __COUNTER__ starts at 0.
diff --git a/clang/test/Modules/Inputs/preprocess/file2.h b/clang/test/Modules/Inputs/preprocess/file2.h
new file mode 100644 (file)
index 0000000..04bf796
--- /dev/null
@@ -0,0 +1,2 @@
+#include "file.h"
+extern int file2;
index a5c5b61..943435a 100644 (file)
@@ -1,2 +1,2 @@
 module fwd { header "fwd.h" export * }
-module file { header "file.h" export * }
+module file { header "file.h" header "file2.h" export * }
index 99fe8cf..337cafb 100644 (file)
@@ -3,10 +3,63 @@
 // RUN: not %clang_cc1 -fmodules -fmodule-name=file -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E 2>&1 | FileCheck %s --check-prefix=MISSING-FWD
 // MISSING-FWD: module 'fwd' is needed
 
-// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodules-cache-path=%t -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E | FileCheck %s
+// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodules-cache-path=%t -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E | FileCheck %s --check-prefix=CHECK --check-prefix=NO-REWRITE
+// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodules-cache-path=%t -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E -frewrite-includes | FileCheck %s --check-prefix=CHECK --check-prefix=REWRITE
+
+// == file.h
 // CHECK: # 1 "<module-includes>"
+// REWRITE: #if 0
+// REWRITE: #include "file.h"
+// REWRITE: #endif
+//
+// FIXME: It would be preferable to consistently put the module begin/end in
+// the same file, but the relative ordering of PP callbacks and module
+// begin/end tokens makes that difficult.
+//
+// REWRITE: #pragma clang module begin file
 // CHECK: # 1 "{{.*}}file.h" 1
+// NO-REWRITE: #pragma clang module begin file
+// NO-REWRITE: # 1 "{{.*}}file.h"{{$}}
+//
 // CHECK: struct __FILE;
-// CHECK: #pragma clang module import fwd /* clang -E: implicit import for #include "fwd.h" */
+// CHECK: #pragma clang module import fwd /* clang {{-E|-frewrite-includes}}: implicit import
 // CHECK: typedef struct __FILE FILE;
+//
+// REWRITE: #pragma clang module end
 // CHECK: # 2 "<module-includes>" 2
+// NO-REWRITE: #pragma clang module end
+
+// == file2.h
+// REWRITE: #if 0
+// REWRITE: #include "file2.h"
+// REWRITE: #endif
+//
+// REWRITE: #pragma clang module begin file
+// CHECK: # 1 "{{.*}}file2.h" 1
+// NO-REWRITE: #pragma clang module begin file
+//
+// ==== recursively re-enter file.h
+// REWRITE: #if 0
+// REWRITE: #include "file.h"
+// REWRITE: #endif
+//
+// REWRITE: #pragma clang module begin file
+// CHECK: # 1 "{{.*}}file.h" 1
+// NO-REWRITE: #pragma clang module begin file
+// NO-REWRITE: # 1 "{{.*}}file.h"{{$}}
+//
+// CHECK: struct __FILE;
+// CHECK: #pragma clang module import fwd /* clang {{-E|-frewrite-includes}}: implicit import
+// CHECK: typedef struct __FILE FILE;
+//
+// REWRITE: #pragma clang module end
+// CHECK: # 2 "{{.*}}file2.h" 2
+// NO-REWRITE: #pragma clang module end
+// NO-REWRITE: # 2 "{{.*}}file2.h"{{$}}
+// ==== return to file2.h
+//
+// CHECK: extern int file2;
+//
+// REWRITE: #pragma clang module end
+// CHECK: # 3 "<module-includes>" 2
+// NO-REWRITE: #pragma clang module end
index d734f66..3763ca3 100644 (file)
@@ -1,11 +1,51 @@
-// RUN: %clang -cc1 -E -fmodules %s -verify
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: echo 'module foo { module a {} module b {} } module bar {}' > %t/module.map
+// RUN: %clang -cc1 -E -fmodules %s -verify -fmodule-name=foo -fmodule-map-file=%t/module.map
+// RUN: %clang -cc1 -E -fmodules %s -verify -fmodule-name=foo -fmodule-map-file=%t/module.map -fmodules-local-submodule-visibility -DLOCAL_VIS
 
 // Just checking the syntax here; the semantics are tested elsewhere.
-#pragma clang module import // expected-error {{expected identifier in module name}}
-#pragma clang module import ! // expected-error {{expected identifier in module name}}
-#pragma clang module import if // expected-error {{expected identifier in module name}}
-#pragma clang module import foo ? bar // expected-error {{expected '.' or end of directive after module name}}
-#pragma clang module import foo. // expected-error {{expected identifier}}
-#pragma clang module import foo.bar.baz.quux // expected-error {{module 'foo' not found}}
-
-#error here // expected-error {{here}}
+#pragma clang module import // expected-error {{expected module name}}
+#pragma clang module import ! // expected-error {{expected module name}}
+#pragma clang module import if // expected-error {{expected module name}}
+#pragma clang module import foo ? bar // expected-warning {{extra tokens at end of #pragma}}
+#pragma clang module import foo. // expected-error {{expected identifier after '.' in module name}}
+#pragma clang module import foo.bar.baz.quux // expected-error {{no submodule named 'bar' in module 'foo'}}
+
+#pragma clang module begin ! // expected-error {{expected module name}}
+
+#pragma clang module begin foo.a blah // expected-warning {{extra tokens}}
+ #pragma clang module begin foo.a // nesting is OK
+  #define X 1 // expected-note 0-1{{previous}}
+  #ifndef X
+  #error X should be defined here
+  #endif
+ #pragma clang module end
+ #ifndef X
+ #error X should still be defined
+ #endif
+#pragma clang module end foo.a // expected-warning {{extra tokens}}
+
+// #pragma clang module begin/end also import the module into the enclosing context
+#ifndef X
+#error X should still be defined
+#endif
+
+#pragma clang module begin foo.b
+ #if defined(X) && defined(LOCAL_VIS)
+ #error under -fmodules-local-submodule-visibility, X should not be defined
+ #endif
+
+ #if !defined(X) && !defined(LOCAL_VIS)
+ #error without -fmodules-local-submodule-visibility, X should still be defined
+ #endif
+
+ #pragma clang module import foo.a
+ #ifndef X
+ #error X should be defined here
+ #endif
+#pragma clang module end
+
+#pragma clang module end // expected-error {{no matching '#pragma clang module begin'}}
+#pragma clang module begin foo.a // expected-error {{no matching '#pragma clang module end'}}