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:
Douglas Gregor 2011-12-31 04:05:44 +00:00
parent 84a5dfdf72
commit 1fb5c3a63a
19 changed files with 385 additions and 36 deletions

View File

@ -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'">;
}

View File

@ -421,6 +421,7 @@ 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 "

View File

@ -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>
@ -58,6 +59,17 @@ 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; }
@ -204,6 +235,16 @@ public:
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;

View File

@ -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);

View File

@ -38,7 +38,12 @@ 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.

View File

@ -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

View File

@ -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)
@ -88,6 +151,17 @@ void Module::print(llvm::raw_ostream &OS, unsigned Indent) const {
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);
OS << "umbrella header \"";

View File

@ -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;

View File

@ -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);
}

View File

@ -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)
@ -223,6 +227,16 @@ 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();

View File

@ -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;
}

View File

@ -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;
@ -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();

View File

@ -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();
@ -372,7 +373,8 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
if (const FileEntry *Header = getFileManager().getFile(Entry->path()))
if (!getSourceManager().hasFileInfo(Header)) {
// Find the
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)
@ -382,6 +384,7 @@ bool Preprocessor::HandleEndOfFile(Token &Result, bool isEndOfMacro) {
}
}
}
}
return true;
}

View File

@ -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;
}
}
}

View File

@ -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();

View File

@ -0,0 +1,5 @@
class CXXOnly {
public:
CXXOnly();
~CXXOnly();
};

View File

@ -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"

View File

@ -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'}}

View File

@ -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