From: Douglas Gregor Date: Sat, 31 Dec 2011 04:05:44 +0000 (+0000) Subject: Implement support for module requirements, which indicate the language X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=51f564f80d9f71e175635b452ffeeeff899e9bf1;p=clang 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/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 5efba22529..82a570472c 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -143,4 +143,6 @@ def warn_missing_submodule : Warning<"missing submodule '%0'">, InGroup; def err_module_map_temp_file : Error< "unable to write temporary module map file '%0'">, DefaultFatal; +def err_module_unavailable : Error<"module '%0' requires feature '%1'">; + } diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 62bd315842..41c67fb1f2 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -421,7 +421,8 @@ def err_mmap_explicit_top_level : Error< "'explicit' is not permitted on top-level modules">; def err_mmap_nested_submodule_id : Error< "qualified module name can only be used to define modules at the top level">; - +def err_mmap_expected_feature : Error<"expected a feature name">; + def warn_auto_module_import : Warning< "treating #%select{include|import|include_next|__include_macros}0 as an " "import of module '%1'">, InGroup, DefaultIgnore; diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h index 96b716e0ce..4e6bba2ed3 100644 --- a/include/clang/Basic/Module.h +++ b/include/clang/Basic/Module.h @@ -29,8 +29,9 @@ namespace llvm { namespace clang { -class FileEntry; class DirectoryEntry; +class FileEntry; +class LangOptions; /// \brief Describes the name of a module. typedef llvm::SmallVector, 2> @@ -57,7 +58,18 @@ public: /// \brief The headers that are part of this module. llvm::SmallVector Headers; - + + /// \brief The set of language features required to use this module. + /// + /// If any of these features is not present, the \c IsAvailable bit + /// will be false to indicate that this (sub)module is not + /// available. + llvm::SmallVector Requires; + + /// \brief Whether this module is available in the current + /// translation unit. + unsigned IsAvailable : 1; + /// \brief Whether this module was loaded from a module file. unsigned IsFromModuleFile : 1; @@ -134,21 +146,40 @@ public: explicit Module(StringRef Name, SourceLocation DefinitionLoc, bool IsFramework) : Name(Name), DefinitionLoc(DefinitionLoc), Parent(0), Umbrella(), - IsFromModuleFile(false), IsFramework(IsFramework), IsExplicit(false), - InferSubmodules(false), InferExplicitSubmodules(false), + IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework), + IsExplicit(false), InferSubmodules(false), InferExplicitSubmodules(false), InferExportWildcard(false), NameVisibility(Hidden) { } /// \brief Construct a new module or submodule. Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, bool IsFramework, bool IsExplicit) : Name(Name), DefinitionLoc(DefinitionLoc), Parent(Parent), - Umbrella(), IsFromModuleFile(false), IsFramework(IsFramework), - IsExplicit(IsExplicit), InferSubmodules(false), + Umbrella(), IsAvailable(true), IsFromModuleFile(false), + IsFramework(IsFramework), IsExplicit(IsExplicit), InferSubmodules(false), InferExplicitSubmodules(false), InferExportWildcard(false), - NameVisibility(Hidden) { } + NameVisibility(Hidden) + { + if (Parent && !Parent->isAvailable()) + IsAvailable = false; + } ~Module(); + /// \brief Determine whether this module is available for use within the + /// current translation unit. + bool isAvailable() const { return IsAvailable; } + + /// \brief Determine whether this module is available for use within the + /// current translation unit. + /// + /// \param LangOpts The language options used for the current + /// translation unit. + /// + /// \param Feature If this module is unavailable, this parameter + /// will be set to one of the features that is required for use of + /// this module (but is not available). + bool isAvailable(const LangOptions &LangOpts, StringRef &Feature) const; + /// \brief Determine whether this module is a submodule. bool isSubModule() const { return Parent != 0; } @@ -203,7 +234,17 @@ public: bool hasUmbrellaDir() const { return Umbrella && Umbrella.is(); } - + + /// \briaf Add the given feature requirement to the list of features + /// required by this module. + /// + /// \param Feature The feature that is required by this module (and + /// its submodules). + /// + /// \param LangOpts The set of language options that will be used to + /// evaluate the availability of this feature. + void addRequirement(StringRef Feature, const LangOptions &LangOpts); + /// \brief Print the module map for this module to the given stream. /// void print(llvm::raw_ostream &OS, unsigned Indent = 0) const; diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h index ade5d2a6f1..10242fa264 100644 --- a/include/clang/Lex/HeaderSearch.h +++ b/include/clang/Lex/HeaderSearch.h @@ -185,7 +185,8 @@ class HeaderSearch { explicit HeaderSearch(const HeaderSearch&); void operator=(const HeaderSearch&); public: - HeaderSearch(FileManager &FM, DiagnosticsEngine &Diags); + HeaderSearch(FileManager &FM, DiagnosticsEngine &Diags, + const LangOptions &LangOpts); ~HeaderSearch(); FileManager &getFileMgr() const { return FileMgr; } @@ -369,6 +370,8 @@ public: bool hasModuleMap(StringRef Filename, const DirectoryEntry *Root); /// \brief Retrieve the module that corresponds to the given file, if any. + /// + /// \param File The header that we wish to map to a module. Module *findModuleForHeader(const FileEntry *File); diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 2d95255290..b017a05e97 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -38,8 +38,13 @@ class ModuleMapParser; class ModuleMap { SourceManager *SourceMgr; llvm::IntrusiveRefCntPtr Diags; - LangOptions LangOpts; - + const LangOptions &LangOpts; + + /// \brief Language options used to parse the module map itself. + /// + /// These are always simple C language options. + LangOptions MMapLangOpts; + /// \brief The top-level modules that are known. llvm::StringMap Modules; @@ -82,7 +87,10 @@ public: /// /// \param DC A diagnostic consumer that will be cloned for use in generating /// diagnostics. - ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC); + /// + /// \param LangOpts Language options for this translation unit. + ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC, + const LangOptions &LangOpts); /// \brief Destroy the module map. /// @@ -96,6 +104,10 @@ public: /// that no module owns this header file. Module *findModuleForHeader(const FileEntry *File); + /// \brief Determine whether the given header is part of a module + /// marked 'unavailable'. + bool isHeaderInUnavailableModule(const FileEntry *Header); + /// \brief Retrieve a module with the given name. /// /// \param The name of the module to look up. @@ -188,7 +200,7 @@ public: /// \brief Adds this header to the given module. void addHeader(Module *Mod, const FileEntry *Header); - + /// \brief Parse the given module map file, and record any modules we /// encounter. /// diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 7fd550f992..bd6e3a2b40 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -527,7 +527,9 @@ namespace clang { SUBMODULE_IMPORTS = 5, /// \brief Specifies the submodules that are re-exported from this /// submodule. - SUBMODULE_EXPORTS = 6 + SUBMODULE_EXPORTS = 6, + /// \brief Specifies a required feature. + SUBMODULE_REQUIRES = 7 }; /// \defgroup ASTAST AST file AST constants 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(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(); } +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 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::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 DiagIDs(new DiagnosticIDs); Diags = llvm::IntrusiveRefCntPtr( new DiagnosticsEngine(DiagIDs)); @@ -90,8 +93,14 @@ ModuleMap::~ModuleMap() { Module *ModuleMap::findModuleForHeader(const FileEntry *File) { llvm::DenseMap::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 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::iterator Known + = Headers.find(Header); + if (Known != Headers.end()) + return !Known->second->isAvailable(); + + const DirectoryEntry *Dir = Header->getDir(); + llvm::SmallVector SkippedDirs; + StringRef DirName = Dir->getName(); + + // Keep walking up the directory hierarchy, looking for a directory with + // an umbrella header. + do { + llvm::DenseMap::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::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 &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(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(); diff --git a/test/Modules/Inputs/DependsOnModule.framework/Headers/cxx_other.h b/test/Modules/Inputs/DependsOnModule.framework/Headers/cxx_other.h new file mode 100644 index 0000000000..724d7987af --- /dev/null +++ b/test/Modules/Inputs/DependsOnModule.framework/Headers/cxx_other.h @@ -0,0 +1,5 @@ +class CXXOnly { + public: + CXXOnly(); + ~CXXOnly(); +}; diff --git a/test/Modules/Inputs/DependsOnModule.framework/module.map b/test/Modules/Inputs/DependsOnModule.framework/module.map index d8fe2fc3f5..2a3dd8038b 100644 --- a/test/Modules/Inputs/DependsOnModule.framework/module.map +++ b/test/Modules/Inputs/DependsOnModule.framework/module.map @@ -4,6 +4,11 @@ framework module DependsOnModule { module * { export * } + explicit module CXX { + requires cplusplus + header "cxx_other.h" + } + explicit framework module SubFramework { umbrella header "SubFramework.h" diff --git a/test/Modules/requires.m b/test/Modules/requires.m new file mode 100644 index 0000000000..ceb03f32d3 --- /dev/null +++ b/test/Modules/requires.m @@ -0,0 +1,5 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -Wauto-import -fmodule-cache-path %t -fauto-module-import -F %S/Inputs %s -verify + +__import_module__ DependsOnModule.CXX; // expected-error{{module 'DependsOnModule.CXX' requires feature 'cplusplus'}} + diff --git a/test/Modules/subframeworks.m b/test/Modules/subframeworks.m index 44b6746a4a..2e149cccdc 100644 --- a/test/Modules/subframeworks.m +++ b/test/Modules/subframeworks.m @@ -1,5 +1,6 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -Wauto-import -fmodule-cache-path %t -fauto-module-import -F %S/Inputs %s -verify +// RUN: %clang_cc1 -x objective-c++ -Wauto-import -fmodule-cache-path %t -fauto-module-import -F %S/Inputs %s -verify __import_module__ DependsOnModule; @@ -14,3 +15,8 @@ void testSubFrameworkAgain() { double *sfo1 = sub_framework_other; } +#ifdef __cplusplus +__import_module__ DependsOnModule.CXX; + +CXXOnly cxxonly; +#endif