diff options
author | Douglas Gregor <dgregor@apple.com> | 2011-12-31 04:05:44 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2011-12-31 04:05:44 +0000 |
commit | 51f564f80d9f71e175635b452ffeeeff899e9bf1 (patch) | |
tree | ada57bc51cc68158361f5d1a49d8de00b6a1e564 /lib/Lex/ModuleMap.cpp | |
parent | 868f65c6bf904591f62a0d69866825d2d3dfd16f (diff) |
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
Diffstat (limited to 'lib/Lex/ModuleMap.cpp')
-rw-r--r-- | lib/Lex/ModuleMap.cpp | 140 |
1 files changed, 134 insertions, 6 deletions
diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 7d093c863c..f43abc8615 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -69,7 +69,10 @@ ModuleMap::resolveExport(Module *Mod, 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 @@ ModuleMap::~ModuleMap() { 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 *ModuleMap::findModuleForHeader(const FileEntry *File) { 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 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) { } 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 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) { 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 @@ namespace clang { /// \brief A token in a module map file. struct MMToken { enum TokenKind { + Comma, EndOfFile, HeaderKeyword, Identifier, @@ -384,6 +461,7 @@ namespace clang { ModuleKeyword, Period, UmbrellaKeyword, + RequiresKeyword, Star, StringLiteral, LBrace, @@ -449,6 +527,7 @@ namespace clang { ModuleId; bool parseModuleId(ModuleId &Id); void parseModuleDecl(); + void parseRequiresDecl(); void parseHeaderDecl(SourceLocation UmbrellaLoc); void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc); void parseExportDecl(); @@ -494,10 +573,15 @@ retry: .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 @@ bool ModuleMapParser::parseModuleId(ModuleId &Id) { /// 'explicit'[opt] 'framework'[opt] 'module' module-id { module-member* } /// /// module-member: +/// requires-declaration /// header-declaration /// submodule-declaration /// export-declaration @@ -755,6 +840,10 @@ void ModuleMapParser::parseModuleDecl() { parseExportDecl(); break; + case MMToken::RequiresKeyword: + parseRequiresDecl(); + break; + case MMToken::UmbrellaKeyword: { SourceLocation UmbrellaLoc = consumeToken(); if (Tok.is(MMToken::HeaderKeyword)) @@ -787,6 +876,43 @@ void ModuleMapParser::parseModuleDecl() { 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 @@ bool ModuleMapParser::parseModuleMapFile() { 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 @@ bool ModuleMap::parseModuleMapFile(const FileEntry *File) { 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(); |