forked from OSchip/llvm-project
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). llvm-svn: 147387
This commit is contained in:
parent
84a5dfdf72
commit
1fb5c3a63a
|
@ -143,4 +143,6 @@ def warn_missing_submodule : Warning<"missing submodule '%0'">,
|
|||
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'">;
|
||||
|
||||
}
|
||||
|
|
|
@ -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<AutoImport>, DefaultIgnore;
|
||||
|
|
|
@ -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<std::pair<std::string, SourceLocation>, 2>
|
||||
|
@ -57,7 +58,18 @@ public:
|
|||
|
||||
/// \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 @@ 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<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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -38,8 +38,13 @@ class ModuleMapParser;
|
|||
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 @@ 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.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class CXXOnly {
|
||||
public:
|
||||
CXXOnly();
|
||||
~CXXOnly();
|
||||
};
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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'}}
|
||||
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue