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 5efba22..82a5704 100644
--- a/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -143,4 +143,6 @@
InGroup<IncompleteUmbrella>;
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 62bd315..41c67fb 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/include/clang/Basic/DiagnosticLexKinds.td
@@ -421,7 +421,8 @@
"'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<AutoImport>, DefaultIgnore;
diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h
index 96b716e0..4e6bba2 100644
--- a/include/clang/Basic/Module.h
+++ b/include/clang/Basic/Module.h
@@ -29,8 +29,9 @@
namespace clang {
-class FileEntry;
class DirectoryEntry;
+class FileEntry;
+class LangOptions;
/// \brief Describes the name of a module.
typedef llvm::SmallVector<std::pair<std::string, SourceLocation>, 2>
@@ -57,7 +58,18 @@
/// \brief The headers that are part of this module.
llvm::SmallVector<const FileEntry *, 2> 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<std::string, 2> 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 @@
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 @@
bool hasUmbrellaDir() const {
return Umbrella && Umbrella.is<const DirectoryEntry *>();
}
-
+
+ /// \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 ade5d2a..10242fa 100644
--- a/include/clang/Lex/HeaderSearch.h
+++ b/include/clang/Lex/HeaderSearch.h
@@ -185,7 +185,8 @@
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 @@
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 2d95255..b017a05 100644
--- a/include/clang/Lex/ModuleMap.h
+++ b/include/clang/Lex/ModuleMap.h
@@ -38,8 +38,13 @@
class ModuleMap {
SourceManager *SourceMgr;
llvm::IntrusiveRefCntPtr<DiagnosticsEngine> 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<Module *> Modules;
@@ -82,7 +87,10 @@
///
/// \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 @@
/// 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 @@
/// \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 7fd550f..bd6e3a2 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -527,7 +527,9 @@
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 69a62d3..feec5f0 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 @@
}
+/// \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 @@
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 @@
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 241a2c3..5a9f077 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -667,7 +667,8 @@
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 12d3685..498f6fb 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -252,7 +252,8 @@
// 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 @@
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 a97a32b..dd98fcf 100644
--- a/lib/Frontend/FrontendActions.cpp
+++ b/lib/Frontend/FrontendActions.cpp
@@ -136,6 +136,10 @@
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 @@
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 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;
+ }
}
}
}
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index 440474b..dd48b2d 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -2523,6 +2523,11 @@
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 @@
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 1302d53..cfc83f7 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -1916,6 +1916,11 @@
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 @@
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 0000000..724d798
--- /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 d8fe2fc..2a3dd80 100644
--- a/test/Modules/Inputs/DependsOnModule.framework/module.map
+++ b/test/Modules/Inputs/DependsOnModule.framework/module.map
@@ -4,6 +4,11 @@
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 0000000..ceb03f3
--- /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 44b6746..2e149cc 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 @@
double *sfo1 = sub_framework_other;
}
+#ifdef __cplusplus
+__import_module__ DependsOnModule.CXX;
+
+CXXOnly cxxonly;
+#endif