diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Basic/Module.cpp | 74 | ||||
-rw-r--r-- | lib/Frontend/ASTUnit.cpp | 3 | ||||
-rw-r--r-- | lib/Frontend/CompilerInstance.cpp | 16 | ||||
-rw-r--r-- | lib/Frontend/FrontendActions.cpp | 16 | ||||
-rw-r--r-- | lib/Lex/HeaderSearch.cpp | 9 | ||||
-rw-r--r-- | lib/Lex/ModuleMap.cpp | 140 | ||||
-rw-r--r-- | lib/Lex/PPLexerChange.cpp | 19 | ||||
-rw-r--r-- | lib/Serialization/ASTReader.cpp | 18 | ||||
-rw-r--r-- | lib/Serialization/ASTWriter.cpp | 14 |
9 files changed, 288 insertions, 21 deletions
diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index 69a62d394a..feec5f0258 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -13,7 +13,11 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/Module.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSwitch.h" using namespace clang; Module::~Module() { @@ -25,6 +29,36 @@ Module::~Module() { } +/// \brief Determine whether a translation unit built using the current +/// language options has the given feature. +static bool hasFeature(StringRef Feature, const LangOptions &LangOpts) { + return llvm::StringSwitch<bool>(Feature) + .Case("blocks", LangOpts.Blocks) + .Case("cplusplus", LangOpts.CPlusPlus) + .Case("cplusplus11", LangOpts.CPlusPlus0x) + .Case("objc", LangOpts.ObjC1) + .Case("objc_arc", LangOpts.ObjCAutoRefCount) + .Default(false); +} + +bool +Module::isAvailable(const LangOptions &LangOpts, StringRef &Feature) const { + if (IsAvailable) + return true; + + for (const Module *Current = this; Current; Current = Current->Parent) { + for (unsigned I = 0, N = Current->Requires.size(); I != N; ++I) { + if (!hasFeature(Current->Requires[I], LangOpts)) { + Feature = Current->Requires[I]; + return false; + } + } + } + + llvm_unreachable("could not find a reason why module is unavailable"); + return false; +} + bool Module::isSubModuleOf(Module *Other) const { const Module *This = this; do { @@ -72,6 +106,35 @@ const DirectoryEntry *Module::getUmbrellaDir() const { return Umbrella.dyn_cast<const DirectoryEntry *>(); } +void Module::addRequirement(StringRef Feature, const LangOptions &LangOpts) { + Requires.push_back(Feature); + + // If this feature is currently available, we're done. + if (hasFeature(Feature, LangOpts)) + return; + + if (!IsAvailable) + return; + + llvm::SmallVector<Module *, 2> Stack; + Stack.push_back(this); + while (!Stack.empty()) { + Module *Current = Stack.back(); + Stack.pop_back(); + + if (!Current->IsAvailable) + continue; + + Current->IsAvailable = false; + for (llvm::StringMap<Module *>::iterator Sub = Current->SubModules.begin(), + SubEnd = Current->SubModules.end(); + Sub != SubEnd; ++Sub) { + if (Sub->second->IsAvailable) + Stack.push_back(Sub->second); + } + } +} + static void printModuleId(llvm::raw_ostream &OS, const ModuleId &Id) { for (unsigned I = 0, N = Id.size(); I != N; ++I) { if (I) @@ -87,6 +150,17 @@ void Module::print(llvm::raw_ostream &OS, unsigned Indent) const { if (IsExplicit) OS << "explicit "; OS << "module " << Name << " {\n"; + + if (!Requires.empty()) { + OS.indent(Indent + 2); + OS << "requires "; + for (unsigned I = 0, N = Requires.size(); I != N; ++I) { + if (I) + OS << ", "; + OS << Requires[I]; + } + OS << "\n"; + } if (const FileEntry *UmbrellaHeader = getUmbrellaHeader()) { OS.indent(Indent + 2); diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 241a2c3296..5a9f077263 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -667,7 +667,8 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, AST->SourceMgr = new SourceManager(AST->getDiagnostics(), AST->getFileManager()); AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager(), - AST->getDiagnostics())); + AST->getDiagnostics(), + AST->ASTFileLangOpts)); for (unsigned I = 0; I != NumRemappedFiles; ++I) { FilenameOrMemBuf fileOrBuf = RemappedFiles[I].second; diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index 12d368501c..498f6fbfc0 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -252,7 +252,8 @@ void CompilerInstance::createPreprocessor() { // Create the Preprocessor. HeaderSearch *HeaderInfo = new HeaderSearch(getFileManager(), - getDiagnostics()); + getDiagnostics(), + getLangOpts()); PP = new Preprocessor(getDiagnostics(), getLangOpts(), &getTarget(), getSourceManager(), *HeaderInfo, *this, PTHMgr, /*OwnsHeaderSearch=*/true); @@ -1284,6 +1285,19 @@ Module *CompilerInstance::loadModule(SourceLocation ImportLoc, return 0; } + + // Check whether this module is available. + StringRef Feature; + if (!Module->isAvailable(getLangOpts(), Feature)) { + getDiagnostics().Report(ImportLoc, diag::err_module_unavailable) + << Module->getFullModuleName() + << Feature + << SourceRange(Path.front().second, Path.back().second); + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = 0; + return 0; + } + ModuleManager->makeModuleVisible(Module, Visibility); } diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index a97a32ba06..dd98fcf3eb 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -136,6 +136,10 @@ ASTConsumer *GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, static void collectModuleHeaderIncludes(const LangOptions &LangOpts, clang::Module *Module, llvm::SmallString<256> &Includes) { + // Don't collect any headers for unavailable modules. + if (!Module->isAvailable()) + return; + // Add includes for each of these headers. for (unsigned I = 0, N = Module->Headers.size(); I != N; ++I) { if (LangOpts.ObjC1) @@ -222,7 +226,17 @@ bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, return false; } - + + // Check whether we can build this module at all. + StringRef Feature; + if (!Module->isAvailable(CI.getLangOpts(), Feature)) { + CI.getDiagnostics().Report(diag::err_module_unavailable) + << Module->getFullModuleName() + << Feature; + + return false; + } + // Do we have an umbrella header for this module? const FileEntry *UmbrellaHeader = Module->getUmbrellaHeader(); diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp index 8a73fb9565..070daa0cd1 100644 --- a/lib/Lex/HeaderSearch.cpp +++ b/lib/Lex/HeaderSearch.cpp @@ -38,9 +38,10 @@ HeaderFileInfo::getControllingMacro(ExternalIdentifierLookup *External) { 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 @@ bool HeaderSearch::hasModuleMap(StringRef FileName, } 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 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(); diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp index e3cc2003ce..11c78703a4 100644 --- a/lib/Lex/PPLexerChange.cpp +++ b/lib/Lex/PPLexerChange.cpp @@ -355,6 +355,7 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) { 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 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) { 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; + } } } } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 440474b587..dd48b2dc56 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -2523,6 +2523,11 @@ void ASTReader::makeModuleVisible(Module *Mod, continue; } + if (!Mod->isAvailable()) { + // Modules that aren't available cannot be made visible. + continue; + } + // Update the module's name visibility. Mod->NameVisibility = NameVisibility; @@ -3247,6 +3252,19 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) { CurrentModule->UnresolvedExports.clear(); break; } + case SUBMODULE_REQUIRES: { + if (First) { + Error("missing submodule metadata record at beginning of block"); + return Failure; + } + + if (!CurrentModule) + break; + + CurrentModule->addRequirement(StringRef(BlobStart, BlobLen), + Context.getLangOptions()); + break; + } } } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 1302d538f5..cfc83f7bc0 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1916,6 +1916,11 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name unsigned UmbrellaDirAbbrev = Stream.EmitAbbrev(Abbrev); + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_REQUIRES)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Feature + unsigned RequiresAbbrev = Stream.EmitAbbrev(Abbrev); + // Write the submodule metadata block. RecordData Record; Record.push_back(getNumberOfModules(WritingModule)); @@ -1947,6 +1952,15 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Record.push_back(Mod->InferExportWildcard); Stream.EmitRecordWithBlob(DefinitionAbbrev, Record, Mod->Name); + // Emit the requirements. + for (unsigned I = 0, N = Mod->Requires.size(); I != N; ++I) { + Record.clear(); + Record.push_back(SUBMODULE_REQUIRES); + Stream.EmitRecordWithBlob(RequiresAbbrev, Record, + Mod->Requires[I].data(), + Mod->Requires[I].size()); + } + // Emit the umbrella header, if there is one. if (const FileEntry *UmbrellaHeader = Mod->getUmbrellaHeader()) { Record.clear(); |