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;
+ }
}
}
}