Implement support for module requirements, which indicate the language
features needed for a particular module to be available. This allows
mixed-language modules, where certain headers only work under some
language variants (e.g., in C++, std.tuple might only be available in
C++11 mode).


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147387 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp
index 8a73fb9..070daa0 100644
--- a/lib/Lex/HeaderSearch.cpp
+++ b/lib/Lex/HeaderSearch.cpp
@@ -38,9 +38,10 @@
 
 ExternalHeaderFileInfoSource::~ExternalHeaderFileInfoSource() {}
 
-HeaderSearch::HeaderSearch(FileManager &FM, DiagnosticsEngine &Diags)
+HeaderSearch::HeaderSearch(FileManager &FM, DiagnosticsEngine &Diags,
+                           const LangOptions &LangOpts)
   : FileMgr(FM), Diags(Diags), FrameworkMap(64), 
-    ModMap(FileMgr, *Diags.getClient()) 
+    ModMap(FileMgr, *Diags.getClient(), LangOpts) 
 {
   AngledDirIdx = 0;
   SystemDirIdx = 0;
@@ -803,8 +804,8 @@
 }
 
 Module *HeaderSearch::findModuleForHeader(const FileEntry *File) {
-  if (Module *Module = ModMap.findModuleForHeader(File))
-    return Module;
+  if (Module *Mod = ModMap.findModuleForHeader(File))
+    return Mod;
   
   return 0;
 }
diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp
index 7d093c8..f43abc8 100644
--- a/lib/Lex/ModuleMap.cpp
+++ b/lib/Lex/ModuleMap.cpp
@@ -69,7 +69,10 @@
   return Module::ExportDecl(Context, Unresolved.Wildcard);
 }
 
-ModuleMap::ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC) {
+ModuleMap::ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC,
+                     const LangOptions &LangOpts)
+  : LangOpts(LangOpts)
+{
   llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagIDs(new DiagnosticIDs);
   Diags = llvm::IntrusiveRefCntPtr<DiagnosticsEngine>(
             new DiagnosticsEngine(DiagIDs));
@@ -90,8 +93,14 @@
 Module *ModuleMap::findModuleForHeader(const FileEntry *File) {
   llvm::DenseMap<const FileEntry *, Module *>::iterator Known
     = Headers.find(File);
-  if (Known != Headers.end())
+  if (Known != Headers.end()) {
+    // If a header corresponds to an unavailable module, don't report
+    // that it maps to anything.
+    if (!Known->second->isAvailable())
+      return 0;
+
     return Known->second;
+  }
   
   const DirectoryEntry *Dir = File->getDir();
   llvm::SmallVector<const DirectoryEntry *, 2> SkippedDirs;
@@ -110,7 +119,7 @@
       Module *UmbrellaModule = Result;
       while (!UmbrellaModule->getUmbrellaDir() && UmbrellaModule->Parent)
         UmbrellaModule = UmbrellaModule->Parent;
-      
+
       if (UmbrellaModule->InferSubmodules) {
         // Infer submodules for each of the directories we found between
         // the directory of the umbrella header and the directory where 
@@ -149,6 +158,12 @@
       }
       
       Headers[File] = Result;
+
+      // If a header corresponds to an unavailable module, don't report
+      // that it maps to anything.
+      if (!Result->isAvailable())
+        return 0;
+
       return Result;
     }
     
@@ -166,6 +181,67 @@
   return 0;
 }
 
+bool ModuleMap::isHeaderInUnavailableModule(const FileEntry *Header) {
+  llvm::DenseMap<const FileEntry *, Module *>::iterator Known
+    = Headers.find(Header);
+  if (Known != Headers.end())
+    return !Known->second->isAvailable();
+  
+  const DirectoryEntry *Dir = Header->getDir();
+  llvm::SmallVector<const DirectoryEntry *, 2> SkippedDirs;
+  StringRef DirName = Dir->getName();
+
+  // Keep walking up the directory hierarchy, looking for a directory with
+  // an umbrella header.
+  do {    
+    llvm::DenseMap<const DirectoryEntry *, Module *>::iterator KnownDir
+      = UmbrellaDirs.find(Dir);
+    if (KnownDir != UmbrellaDirs.end()) {
+      Module *Found = KnownDir->second;
+      if (!Found->isAvailable())
+        return true;
+
+      // Search up the module stack until we find a module with an umbrella
+      // directory.
+      Module *UmbrellaModule = Found;
+      while (!UmbrellaModule->getUmbrellaDir() && UmbrellaModule->Parent)
+        UmbrellaModule = UmbrellaModule->Parent;
+
+      if (UmbrellaModule->InferSubmodules) {
+        for (unsigned I = SkippedDirs.size(); I != 0; --I) {
+          // Find or create the module that corresponds to this directory name.
+          StringRef Name = llvm::sys::path::stem(SkippedDirs[I-1]->getName());
+          Found = lookupModuleQualified(Name, Found);
+          if (!Found)
+            return false;
+          if (!Found->isAvailable())
+            return true;
+        }
+        
+        // Infer a submodule with the same name as this header file.
+        StringRef Name = llvm::sys::path::stem(Header->getName());
+        Found = lookupModuleQualified(Name, Found);
+        if (!Found)
+          return false;
+      }
+
+      return !Found->isAvailable();
+    }
+    
+    SkippedDirs.push_back(Dir);
+    
+    // Retrieve our parent path.
+    DirName = llvm::sys::path::parent_path(DirName);
+    if (DirName.empty())
+      break;
+    
+    // Resolve the parent path to a directory entry.
+    Dir = SourceMgr->getFileManager().getDirectory(DirName);
+  } while (Dir);
+  
+  return false;
+}
+
 Module *ModuleMap::findModule(StringRef Name) {
   llvm::StringMap<Module *>::iterator Known = Modules.find(Name);
   if (Known != Modules.end())
@@ -375,6 +451,7 @@
   /// \brief A token in a module map file.
   struct MMToken {
     enum TokenKind {
+      Comma,
       EndOfFile,
       HeaderKeyword,
       Identifier,
@@ -384,6 +461,7 @@
       ModuleKeyword,
       Period,
       UmbrellaKeyword,
+      RequiresKeyword,
       Star,
       StringLiteral,
       LBrace,
@@ -449,6 +527,7 @@
       ModuleId;
     bool parseModuleId(ModuleId &Id);
     void parseModuleDecl();
+    void parseRequiresDecl();
     void parseHeaderDecl(SourceLocation UmbrellaLoc);
     void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);
     void parseExportDecl();
@@ -494,10 +573,15 @@
                  .Case("export", MMToken::ExportKeyword)
                  .Case("framework", MMToken::FrameworkKeyword)
                  .Case("module", MMToken::ModuleKeyword)
+                 .Case("requires", MMToken::RequiresKeyword)
                  .Case("umbrella", MMToken::UmbrellaKeyword)
                  .Default(MMToken::Identifier);
     break;
-      
+
+  case tok::comma:
+    Tok.Kind = MMToken::Comma;
+    break;
+
   case tok::eof:
     Tok.Kind = MMToken::EndOfFile;
     break;
@@ -614,6 +698,7 @@
 ///     'explicit'[opt] 'framework'[opt] 'module' module-id { module-member* }
 ///
 ///   module-member:
+///     requires-declaration
 ///     header-declaration
 ///     submodule-declaration
 ///     export-declaration
@@ -755,6 +840,10 @@
       parseExportDecl();
       break;
         
+    case MMToken::RequiresKeyword:
+      parseRequiresDecl();
+      break;
+
     case MMToken::UmbrellaKeyword: {
       SourceLocation UmbrellaLoc = consumeToken();
       if (Tok.is(MMToken::HeaderKeyword))
@@ -787,6 +876,43 @@
   ActiveModule = PreviousActiveModule;
 }
 
+/// \brief Parse a requires declaration.
+///
+///   requires-declaration:
+///     'requires' feature-list
+///
+///   feature-list:
+///     identifier ',' feature-list
+///     identifier
+void ModuleMapParser::parseRequiresDecl() {
+  assert(Tok.is(MMToken::RequiresKeyword));
+
+  // Parse 'requires' keyword.
+  consumeToken();
+
+  // Parse the feature-list.
+  do {
+    if (!Tok.is(MMToken::Identifier)) {
+      Diags.Report(Tok.getLocation(), diag::err_mmap_expected_feature);
+      HadError = true;
+      return;
+    }
+
+    // Consume the feature name.
+    std::string Feature = Tok.getString();
+    consumeToken();
+
+    // Add this feature.
+    ActiveModule->addRequirement(Feature, Map.LangOpts);
+
+    if (!Tok.is(MMToken::Comma))
+      break;
+
+    // Consume the comma.
+    consumeToken();
+  } while (true);
+}
+
 /// \brief Append to \p Paths the set of paths needed to get to the 
 /// subframework in which the given module lives.
 void appendSubframeworkPaths(Module *Mod, llvm::SmallVectorImpl<char> &Path) {
@@ -1123,12 +1249,14 @@
       parseModuleDecl();
       break;
       
+    case MMToken::Comma:
     case MMToken::ExportKeyword:
     case MMToken::HeaderKeyword:
     case MMToken::Identifier:
     case MMToken::LBrace:
     case MMToken::Period:
     case MMToken::RBrace:
+    case MMToken::RequiresKeyword:
     case MMToken::Star:
     case MMToken::StringLiteral:
     case MMToken::UmbrellaKeyword:
@@ -1149,8 +1277,8 @@
     return true;
   
   // Parse this module map file.
-  Lexer L(ID, SourceMgr->getBuffer(ID), *SourceMgr, LangOpts);
-  Diags->getClient()->BeginSourceFile(LangOpts);
+  Lexer L(ID, SourceMgr->getBuffer(ID), *SourceMgr, MMapLangOpts);
+  Diags->getClient()->BeginSourceFile(MMapLangOpts);
   ModuleMapParser Parser(L, *SourceMgr, *Diags, *this, File->getDir());
   bool Result = Parser.parseModuleMapFile();
   Diags->getClient()->EndSourceFile();
diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp
index e3cc200..11c7870 100644
--- a/lib/Lex/PPLexerChange.cpp
+++ b/lib/Lex/PPLexerChange.cpp
@@ -355,6 +355,7 @@
       if (getDiagnostics().getDiagnosticLevel(
             diag::warn_uncovered_module_header, 
             StartLoc) != DiagnosticsEngine::Ignored) {
+        ModuleMap &ModMap = getHeaderSearchInfo().getModuleMap();
         typedef llvm::sys::fs::recursive_directory_iterator
           recursive_directory_iterator;
         const DirectoryEntry *Dir = Mod->getUmbrellaDir();
@@ -363,20 +364,22 @@
              Entry != End && !EC; Entry.increment(EC)) {
           using llvm::StringSwitch;
           
-          // Check whether this entry has an extension typically associated with 
+          // Check whether this entry has an extension typically associated with
           // headers.
           if (!StringSwitch<bool>(llvm::sys::path::extension(Entry->path()))
-              .Cases(".h", ".H", ".hh", ".hpp", true)
-              .Default(false))
+                 .Cases(".h", ".H", ".hh", ".hpp", true)
+                 .Default(false))
             continue;
 
           if (const FileEntry *Header = getFileManager().getFile(Entry->path()))
             if (!getSourceManager().hasFileInfo(Header)) {
-              // Find the 
-              llvm::SmallString<128> RelativePath;
-              computeRelativePath(FileMgr, Dir, Header, RelativePath);              
-              Diag(StartLoc, diag::warn_uncovered_module_header)
-                << RelativePath;
+              if (!ModMap.isHeaderInUnavailableModule(Header)) {
+                // Find the relative path that would access this header.
+                llvm::SmallString<128> RelativePath;
+                computeRelativePath(FileMgr, Dir, Header, RelativePath);              
+                Diag(StartLoc, diag::warn_uncovered_module_header)
+                  << RelativePath;
+              }
             }
         }
       }