Add #pragma clang module build/endbuild pragmas for performing a module build

as part of a compilation.

This is intended for two purposes:

1) Writing self-contained test cases for modules: we can now write a single
source file test that builds some number of module files on the side and
imports them.

2) Debugging / test case reduction. A single-source testcase is much more
amenable to reduction, compared to a VFS tarball or .pcm files.

llvm-svn: 305101
This commit is contained in:
Richard Smith 2017-06-09 19:22:32 +00:00
parent 01a48b5dfd
commit 5d2ed48987
13 changed files with 377 additions and 168 deletions

View File

@ -527,6 +527,10 @@ def err_pp_module_end_without_module_begin : Error<
"'#pragma clang module end'">;
def note_pp_module_begin_here : Note<
"entering module '%0' due to this pragma">;
def err_pp_module_build_pth : Error<
"'#pragma clang module build' not supported in pretokenized header">;
def err_pp_module_build_missing_end : Error<
"no matching '#pragma clang module endbuild' for this '#pragma clang module build'">;
def err_defined_macro_name : Error<"'defined' cannot be used as a macro name">;
def err_paste_at_start : Error<

View File

@ -67,7 +67,7 @@ class FileSystem;
/// \brief Utility class for loading a ASTContext from an AST file.
///
class ASTUnit : public ModuleLoader {
class ASTUnit {
public:
struct StandaloneFixIt {
std::pair<unsigned, unsigned> RemoveRange;
@ -119,10 +119,13 @@ private:
/// LoadFromCommandLine available.
std::shared_ptr<CompilerInvocation> Invocation;
/// Fake module loader: the AST unit doesn't need to load any modules.
TrivialModuleLoader ModuleLoader;
// OnlyLocalDecls - when true, walking this AST should only visit declarations
// that come from the AST itself, not from included precompiled headers.
// FIXME: This is temporary; eventually, CIndex will always do this.
bool OnlyLocalDecls;
bool OnlyLocalDecls;
/// \brief Whether to capture any diagnostics produced.
bool CaptureDiagnostics;
@ -496,7 +499,7 @@ public:
};
friend class ConcurrencyCheck;
~ASTUnit() override;
~ASTUnit();
bool isMainFileAST() const { return MainFileIsAST; }
@ -945,21 +948,6 @@ public:
///
/// \returns True if an error occurred, false otherwise.
bool serialize(raw_ostream &OS);
ModuleLoadResult loadModule(SourceLocation ImportLoc, ModuleIdPath Path,
Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) override {
// ASTUnit doesn't know how to load modules (not that this matters).
return ModuleLoadResult();
}
void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc) override {}
GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override
{ return nullptr; }
bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override
{ return 0; }
};
} // namespace clang

View File

@ -136,6 +136,10 @@ class CompilerInstance : public ModuleLoader {
/// along with the module map
llvm::DenseMap<const IdentifierInfo *, Module *> KnownModules;
/// \brief The set of top-level modules that has already been built on the
/// fly as part of this overall compilation action.
std::map<std::string, std::string> BuiltModules;
/// \brief The location of the module-import keyword for the last module
/// import.
SourceLocation LastModuleImportLoc;
@ -773,6 +777,9 @@ public:
Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) override;
void loadModuleFromSource(SourceLocation ImportLoc, StringRef ModuleName,
StringRef Source) override;
void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc) override;

View File

@ -109,6 +109,16 @@ public:
Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) = 0;
/// Attempt to load the given module from the specified source buffer. Does
/// not make any submodule visible; for that, use loadModule or
/// makeModuleVisible.
///
/// \param Loc The location at which the module was loaded.
/// \param ModuleName The name of the module to build.
/// \param Source The source of the module: a (preprocessed) module map.
virtual void loadModuleFromSource(SourceLocation Loc, StringRef ModuleName,
StringRef Source) = 0;
/// \brief Make the given module visible.
virtual void makeModuleVisible(Module *Mod,
Module::NameVisibilityKind Visibility,
@ -136,6 +146,30 @@ public:
bool HadFatalFailure;
};
/// A module loader that doesn't know how to load modules.
class TrivialModuleLoader : public ModuleLoader {
public:
ModuleLoadResult loadModule(SourceLocation ImportLoc, ModuleIdPath Path,
Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) override {
return ModuleLoadResult();
}
void loadModuleFromSource(SourceLocation ImportLoc, StringRef ModuleName,
StringRef Source) override {}
void makeModuleVisible(Module *Mod, Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc) override {}
GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override {
return nullptr;
}
bool lookupMissingImports(StringRef Name,
SourceLocation TriggerLoc) override {
return 0;
}
};
}

View File

@ -2028,6 +2028,7 @@ public:
void HandlePragmaPushMacro(Token &Tok);
void HandlePragmaPopMacro(Token &Tok);
void HandlePragmaIncludeAlias(Token &Tok);
void HandlePragmaModuleBuild(Token &Tok);
IdentifierInfo *ParsePragmaPushOrPopMacro(Token &Tok);
// Return true and store the first token only if any CommentHandler

View File

@ -716,7 +716,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
AST->PP = std::make_shared<Preprocessor>(
AST->PPOpts, AST->getDiagnostics(), *AST->LangOpts,
AST->getSourceManager(), *AST->PCMCache, HeaderInfo, *AST,
AST->getSourceManager(), *AST->PCMCache, HeaderInfo, AST->ModuleLoader,
/*IILookup=*/nullptr,
/*OwnsHeaderSearch=*/false);
Preprocessor &PP = *AST->PP;

View File

@ -667,6 +667,8 @@ void CompilerInstance::clearOutputFiles(bool EraseFiles) {
llvm::sys::fs::remove(OF.Filename);
}
OutputFiles.clear();
for (auto &Module : BuiltModules)
llvm::sys::fs::remove(Module.second);
NonSeekStream.reset();
}
@ -1029,13 +1031,14 @@ static InputKind::Language getLanguageFromOptions(const LangOptions &LangOpts) {
/// \brief Compile a module file for the given module, using the options
/// provided by the importing compiler instance. Returns true if the module
/// was built without errors.
static bool compileModuleImpl(CompilerInstance &ImportingInstance,
SourceLocation ImportLoc,
Module *Module,
StringRef ModuleFileName) {
ModuleMap &ModMap
= ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
static bool
compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc,
StringRef ModuleName, FrontendInputFile Input,
StringRef OriginalModuleMapFile, StringRef ModuleFileName,
llvm::function_ref<void(CompilerInstance &)> PreBuildStep =
[](CompilerInstance &) {},
llvm::function_ref<void(CompilerInstance &)> PostBuildStep =
[](CompilerInstance &) {}) {
// Construct a compiler invocation for creating this module.
auto Invocation =
std::make_shared<CompilerInvocation>(ImportingInstance.getInvocation());
@ -1060,7 +1063,7 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
PPOpts.Macros.end());
// Note the name of the module we're building.
Invocation->getLangOpts()->CurrentModule = Module->getTopLevelModuleName();
Invocation->getLangOpts()->CurrentModule = ModuleName;
// Make sure that the failed-module structure has been allocated in
// the importing instance, and propagate the pointer to the newly-created
@ -1080,13 +1083,10 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
FrontendOpts.DisableFree = false;
FrontendOpts.GenerateGlobalModuleIndex = false;
FrontendOpts.BuildingImplicitModule = true;
FrontendOpts.OriginalModuleMap =
ModMap.getModuleMapFileForUniquing(Module)->getName();
FrontendOpts.OriginalModuleMap = OriginalModuleMapFile;
// Force implicitly-built modules to hash the content of the module file.
HSOpts.ModulesHashContent = true;
FrontendOpts.Inputs.clear();
InputKind IK(getLanguageFromOptions(*Invocation->getLangOpts()),
InputKind::ModuleMap);
FrontendOpts.Inputs = {Input};
// Don't free the remapped file buffers; they are owned by our caller.
PPOpts.RetainRemappedFileBuffers = true;
@ -1117,7 +1117,7 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
SourceManager &SourceMgr = Instance.getSourceManager();
SourceMgr.setModuleBuildStack(
ImportingInstance.getSourceManager().getModuleBuildStack());
SourceMgr.pushModuleBuildStack(Module->getTopLevelModuleName(),
SourceMgr.pushModuleBuildStack(ModuleName,
FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager()));
// If we're collecting module dependencies, we need to share a collector
@ -1126,32 +1126,11 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
Instance.setModuleDepCollector(ImportingInstance.getModuleDepCollector());
Inv.getDependencyOutputOpts() = DependencyOutputOptions();
// Get or create the module map that we'll use to build this module.
std::string InferredModuleMapContent;
if (const FileEntry *ModuleMapFile =
ModMap.getContainingModuleMapFile(Module)) {
// Use the module map where this module resides.
FrontendOpts.Inputs.emplace_back(ModuleMapFile->getName(), IK,
+Module->IsSystem);
} else {
SmallString<128> FakeModuleMapFile(Module->Directory->getName());
llvm::sys::path::append(FakeModuleMapFile, "__inferred_module.map");
FrontendOpts.Inputs.emplace_back(FakeModuleMapFile, IK, +Module->IsSystem);
llvm::raw_string_ostream OS(InferredModuleMapContent);
Module->print(OS);
OS.flush();
std::unique_ptr<llvm::MemoryBuffer> ModuleMapBuffer =
llvm::MemoryBuffer::getMemBuffer(InferredModuleMapContent);
ModuleMapFile = Instance.getFileManager().getVirtualFile(
FakeModuleMapFile, InferredModuleMapContent.size(), 0);
SourceMgr.overrideFileContents(ModuleMapFile, std::move(ModuleMapBuffer));
}
ImportingInstance.getDiagnostics().Report(ImportLoc,
diag::remark_module_build)
<< Module->Name << ModuleFileName;
<< ModuleName << ModuleFileName;
PreBuildStep(Instance);
// Execute the action to actually build the module in-place. Use a separate
// thread so that we get a stack large enough.
@ -1164,9 +1143,11 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
},
ThreadStackSize);
PostBuildStep(Instance);
ImportingInstance.getDiagnostics().Report(ImportLoc,
diag::remark_module_build_done)
<< Module->Name;
<< ModuleName;
// Delete the temporary module map file.
// FIXME: Even though we're executing under crash protection, it would still
@ -1174,13 +1155,66 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
// doesn't make sense for all clients, so clean this up manually.
Instance.clearOutputFiles(/*EraseFiles=*/true);
return !Instance.getDiagnostics().hasErrorOccurred();
}
/// \brief Compile a module file for the given module, using the options
/// provided by the importing compiler instance. Returns true if the module
/// was built without errors.
static bool compileModuleImpl(CompilerInstance &ImportingInstance,
SourceLocation ImportLoc,
Module *Module,
StringRef ModuleFileName) {
InputKind IK(getLanguageFromOptions(ImportingInstance.getLangOpts()),
InputKind::ModuleMap);
// Get or create the module map that we'll use to build this module.
ModuleMap &ModMap
= ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
bool Result;
if (const FileEntry *ModuleMapFile =
ModMap.getContainingModuleMapFile(Module)) {
// Use the module map where this module resides.
Result = compileModuleImpl(
ImportingInstance, ImportLoc, Module->getTopLevelModuleName(),
FrontendInputFile(ModuleMapFile->getName(), IK, +Module->IsSystem),
ModMap.getModuleMapFileForUniquing(Module)->getName(),
ModuleFileName);
} else {
// FIXME: We only need to fake up an input file here as a way of
// transporting the module's directory to the module map parser. We should
// be able to do that more directly, and parse from a memory buffer without
// inventing this file.
SmallString<128> FakeModuleMapFile(Module->Directory->getName());
llvm::sys::path::append(FakeModuleMapFile, "__inferred_module.map");
std::string InferredModuleMapContent;
llvm::raw_string_ostream OS(InferredModuleMapContent);
Module->print(OS);
OS.flush();
Result = compileModuleImpl(
ImportingInstance, ImportLoc, Module->getTopLevelModuleName(),
FrontendInputFile(FakeModuleMapFile, IK, +Module->IsSystem),
ModMap.getModuleMapFileForUniquing(Module)->getName(),
ModuleFileName,
[&](CompilerInstance &Instance) {
std::unique_ptr<llvm::MemoryBuffer> ModuleMapBuffer =
llvm::MemoryBuffer::getMemBuffer(InferredModuleMapContent);
ModuleMapFile = Instance.getFileManager().getVirtualFile(
FakeModuleMapFile, InferredModuleMapContent.size(), 0);
Instance.getSourceManager().overrideFileContents(
ModuleMapFile, std::move(ModuleMapBuffer));
});
}
// We've rebuilt a module. If we're allowed to generate or update the global
// module index, record that fact in the importing compiler instance.
if (ImportingInstance.getFrontendOpts().GenerateGlobalModuleIndex) {
ImportingInstance.setBuildGlobalModuleIndex(true);
}
return !Instance.getDiagnostics().hasErrorOccurred();
return Result;
}
static bool compileAndLoadModule(CompilerInstance &ImportingInstance,
@ -1586,24 +1620,36 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
PP->getHeaderSearchInfo().getHeaderSearchOpts();
std::string ModuleFileName;
bool LoadFromPrebuiltModulePath = false;
// We try to load the module from the prebuilt module paths. If not
// successful, we then try to find it in the module cache.
if (!HSOpts.PrebuiltModulePaths.empty()) {
// Load the module from the prebuilt module path.
enum ModuleSource {
ModuleNotFound, ModuleCache, PrebuiltModulePath, ModuleBuildPragma
} Source = ModuleNotFound;
// Check to see if the module has been built as part of this compilation
// via a module build pragma.
auto BuiltModuleIt = BuiltModules.find(ModuleName);
if (BuiltModuleIt != BuiltModules.end()) {
ModuleFileName = BuiltModuleIt->second;
Source = ModuleBuildPragma;
}
// Try to load the module from the prebuilt module path.
if (Source == ModuleNotFound && !HSOpts.PrebuiltModulePaths.empty()) {
ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName(
ModuleName, "", /*UsePrebuiltPath*/ true);
if (!ModuleFileName.empty())
LoadFromPrebuiltModulePath = true;
Source = PrebuiltModulePath;
}
if (!LoadFromPrebuiltModulePath && Module) {
// Load the module from the module cache.
// Try to load the module from the module cache.
if (Source == ModuleNotFound && Module) {
ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName(Module);
} else if (!LoadFromPrebuiltModulePath) {
Source = ModuleCache;
}
if (Source == ModuleNotFound) {
// We can't find a module, error out here.
getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found)
<< ModuleName
<< SourceRange(ImportLoc, ModuleNameLoc);
<< ModuleName << SourceRange(ImportLoc, ModuleNameLoc);
ModuleBuildFailed = true;
return ModuleLoadResult();
}
@ -1631,20 +1677,20 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
*FrontendTimerGroup);
llvm::TimeRegion TimeLoading(FrontendTimerGroup ? &Timer : nullptr);
// Try to load the module file. If we are trying to load from the prebuilt
// module path, we don't have the module map files and don't know how to
// rebuild modules.
unsigned ARRFlags = LoadFromPrebuiltModulePath ?
ASTReader::ARR_ConfigurationMismatch :
ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing;
// Try to load the module file. If we are not trying to load from the
// module cache, we don't know how to rebuild modules.
unsigned ARRFlags = Source == ModuleCache ?
ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing :
ASTReader::ARR_ConfigurationMismatch;
switch (ModuleManager->ReadAST(ModuleFileName,
LoadFromPrebuiltModulePath ?
serialization::MK_PrebuiltModule :
serialization::MK_ImplicitModule,
ImportLoc,
ARRFlags)) {
Source == PrebuiltModulePath
? serialization::MK_PrebuiltModule
: Source == ModuleBuildPragma
? serialization::MK_ExplicitModule
: serialization::MK_ImplicitModule,
ImportLoc, ARRFlags)) {
case ASTReader::Success: {
if (LoadFromPrebuiltModulePath && !Module) {
if (Source != ModuleCache && !Module) {
Module = PP->getHeaderSearchInfo().lookupModule(ModuleName);
if (!Module || !Module->getASTFile() ||
FileMgr->getFile(ModuleFileName) != Module->getASTFile()) {
@ -1662,10 +1708,10 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
case ASTReader::OutOfDate:
case ASTReader::Missing: {
if (LoadFromPrebuiltModulePath) {
// We can't rebuild the module without a module map. Since ReadAST
// already produces diagnostics for these two cases, we simply
// error out here.
if (Source != ModuleCache) {
// We don't know the desired configuration for this module and don't
// necessarily even have a module map. Since ReadAST already produces
// diagnostics for these two cases, we simply error out here.
ModuleBuildFailed = true;
KnownModules[Path[0].first] = nullptr;
return ModuleLoadResult();
@ -1722,7 +1768,9 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
}
case ASTReader::ConfigurationMismatch:
if (LoadFromPrebuiltModulePath)
if (Source == PrebuiltModulePath)
// FIXME: We shouldn't be setting HadFatalFailure below if we only
// produce a warning here!
getDiagnostics().Report(SourceLocation(),
diag::warn_module_config_mismatch)
<< ModuleFileName;
@ -1751,7 +1799,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
// If we never found the module, fail.
if (!Module)
return ModuleLoadResult();
// Verify that the rest of the module path actually corresponds to
// a submodule.
if (Path.size() > 1) {
@ -1848,6 +1896,54 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
return LastModuleImportResult;
}
void CompilerInstance::loadModuleFromSource(SourceLocation ImportLoc,
StringRef ModuleName,
StringRef Source) {
// FIXME: Using a randomized filename here means that our intermediate .pcm
// output is nondeterministic (as .pcm files refer to each other by name).
// Can this affect the output in any way?
SmallString<128> ModuleFileName;
if (std::error_code EC = llvm::sys::fs::createTemporaryFile(
ModuleName, "pcm", ModuleFileName)) {
getDiagnostics().Report(ImportLoc, diag::err_fe_unable_to_open_output)
<< ModuleFileName << EC.message();
return;
}
std::string ModuleMapFileName = (ModuleName + ".map").str();
FrontendInputFile Input(
ModuleMapFileName,
InputKind(getLanguageFromOptions(*Invocation->getLangOpts()),
InputKind::ModuleMap, /*Preprocessed*/true));
std::string NullTerminatedSource(Source.str());
auto PreBuildStep = [&](CompilerInstance &Other) {
// Create a virtual file containing our desired source.
// FIXME: We shouldn't need to do this.
const FileEntry *ModuleMapFile = Other.getFileManager().getVirtualFile(
ModuleMapFileName, NullTerminatedSource.size(), 0);
Other.getSourceManager().overrideFileContents(
ModuleMapFile,
llvm::MemoryBuffer::getMemBuffer(NullTerminatedSource.c_str()));
Other.BuiltModules = std::move(BuiltModules);
};
auto PostBuildStep = [this](CompilerInstance &Other) {
BuiltModules = std::move(Other.BuiltModules);
// Make sure the child build action doesn't delete the .pcms.
Other.BuiltModules.clear();
};
// Build the module, inheriting any modules that we've built locally.
if (compileModuleImpl(*this, ImportLoc, ModuleName, Input, StringRef(),
ModuleFileName, PreBuildStep, PostBuildStep)) {
BuiltModules[ModuleName] = ModuleFileName.str();
llvm::sys::RemoveFileOnSignal(ModuleFileName);
}
}
void CompilerInstance::makeModuleVisible(Module *Mod,
Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc) {

View File

@ -754,6 +754,88 @@ void Preprocessor::HandlePragmaIncludeAlias(Token &Tok) {
getHeaderSearchInfo().AddIncludeAlias(OriginalSource, ReplaceFileName);
}
void Preprocessor::HandlePragmaModuleBuild(Token &Tok) {
SourceLocation Loc = Tok.getLocation();
LexUnexpandedToken(Tok);
if (Tok.isAnnotation() || !Tok.getIdentifierInfo()) {
Diag(Tok.getLocation(), diag::err_pp_expected_module_name) << true;
return;
}
IdentifierInfo *ModuleName = Tok.getIdentifierInfo();
LexUnexpandedToken(Tok);
if (Tok.isNot(tok::eod)) {
Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
DiscardUntilEndOfDirective();
}
if (CurPTHLexer) {
// FIXME: Support this somehow?
Diag(Loc, diag::err_pp_module_build_pth);
return;
}
CurLexer->LexingRawMode = true;
auto TryConsumeIdentifier = [&](StringRef Ident) -> bool {
if (Tok.getKind() != tok::raw_identifier ||
Tok.getRawIdentifier() != Ident)
return false;
CurLexer->Lex(Tok);
return true;
};
// Scan forward looking for the end of the module.
const char *Start = CurLexer->getBufferLocation();
const char *End = nullptr;
unsigned NestingLevel = 1;
while (true) {
End = CurLexer->getBufferLocation();
CurLexer->Lex(Tok);
if (Tok.is(tok::eof)) {
Diag(Loc, diag::err_pp_module_build_missing_end);
break;
}
if (Tok.isNot(tok::hash) || !Tok.isAtStartOfLine()) {
// Token was part of module; keep going.
continue;
}
// We hit something directive-shaped; check to see if this is the end
// of the module build.
CurLexer->ParsingPreprocessorDirective = true;
CurLexer->Lex(Tok);
if (TryConsumeIdentifier("pragma") && TryConsumeIdentifier("clang") &&
TryConsumeIdentifier("module")) {
if (TryConsumeIdentifier("build"))
// #pragma clang module build -> entering a nested module build.
++NestingLevel;
else if (TryConsumeIdentifier("endbuild")) {
// #pragma clang module endbuild -> leaving a module build.
if (--NestingLevel == 0)
break;
}
// We should either be looking at the EOD or more of the current directive
// preceding the EOD. Either way we can ignore this token and keep going.
assert(Tok.getKind() != tok::eof && "missing EOD before EOF");
}
}
CurLexer->LexingRawMode = false;
// Load the extracted text as a preprocessed module.
assert(CurLexer->getBuffer().begin() <= Start &&
Start <= CurLexer->getBuffer().end() &&
CurLexer->getBuffer().begin() <= End &&
End <= CurLexer->getBuffer().end() &&
"module source range not contained within same file buffer");
TheModuleLoader.loadModuleFromSource(Loc, ModuleName->getName(),
StringRef(Start, End - Start));
}
/// AddPragmaHandler - Add the specified pragma handler to the preprocessor.
/// If 'Namespace' is non-null, then it is a token required to exist on the
/// pragma line before the pragma string starts, e.g. "STDC" or "GCC".
@ -1442,6 +1524,39 @@ struct PragmaModuleEndHandler : public PragmaHandler {
}
};
/// Handle the clang \#pragma module build extension.
struct PragmaModuleBuildHandler : public PragmaHandler {
PragmaModuleBuildHandler() : PragmaHandler("build") {}
void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
Token &Tok) override {
PP.HandlePragmaModuleBuild(Tok);
}
};
/// Handle the clang \#pragma module load extension.
struct PragmaModuleLoadHandler : public PragmaHandler {
PragmaModuleLoadHandler() : PragmaHandler("load") {}
void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer,
Token &Tok) override {
SourceLocation Loc = Tok.getLocation();
// Read the module name.
llvm::SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 8>
ModuleName;
if (LexModuleName(PP, Tok, ModuleName))
return;
if (Tok.isNot(tok::eod))
PP.Diag(Tok, diag::ext_pp_extra_tokens_at_eol) << "pragma";
// Load the module, don't make it visible.
PP.getModuleLoader().loadModule(Loc, ModuleName, Module::Hidden,
/*IsIncludeDirective=*/false);
}
};
/// PragmaPushMacroHandler - "\#pragma push_macro" saves the value of the
/// macro on the top of the stack.
struct PragmaPushMacroHandler : public PragmaHandler {
@ -1671,6 +1786,8 @@ void Preprocessor::RegisterBuiltinPragmas() {
ModuleHandler->AddPragma(new PragmaModuleImportHandler());
ModuleHandler->AddPragma(new PragmaModuleBeginHandler());
ModuleHandler->AddPragma(new PragmaModuleEndHandler());
ModuleHandler->AddPragma(new PragmaModuleBuildHandler());
ModuleHandler->AddPragma(new PragmaModuleLoadHandler());
AddPragmaHandler("STDC", new PragmaSTDC_FENV_ACCESSHandler());
AddPragmaHandler("STDC", new PragmaSTDC_CX_LIMITED_RANGEHandler());

View File

@ -0,0 +1,35 @@
// RUN: %clang_cc1 -std=c++1z %s -verify
#pragma clang module build baz
module baz {}
#pragma clang module endbuild // baz
#pragma clang module build foo
module foo { module bar {} }
#pragma clang module contents
#pragma clang module begin foo.bar
// Can import baz here even though it was created in an outer build.
#pragma clang module import baz
#pragma clang module build bar
module bar {}
#pragma clang module contents
#pragma clang module begin bar
extern int n;
#pragma clang module end
#pragma clang module endbuild // bar
#pragma clang module import bar
constexpr int *f() { return &n; }
#pragma clang module end
#pragma clang module endbuild // foo
#pragma clang module import bar
#pragma clang module import foo.bar
static_assert(f() == &n);
#pragma clang module build // expected-error {{expected module name}}
#pragma clang module build unterminated // expected-error {{no matching '#pragma clang module endbuild'}}

View File

@ -51,24 +51,6 @@ protected:
IntrusiveRefCntPtr<TargetInfo> Target;
};
class VoidModuleLoader : public ModuleLoader {
ModuleLoadResult loadModule(SourceLocation ImportLoc,
ModuleIdPath Path,
Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) override {
return ModuleLoadResult();
}
void makeModuleVisible(Module *Mod,
Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc) override { }
GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override
{ return nullptr; }
bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override
{ return 0; }
};
TEST_F(SourceManagerTest, isBeforeInTranslationUnit) {
const char *source =
"#define M(x) [x]\n"
@ -78,7 +60,7 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnit) {
FileID mainFileID = SourceMgr.createFileID(std::move(Buf));
SourceMgr.setMainFileID(mainFileID);
VoidModuleLoader ModLoader;
TrivialModuleLoader ModLoader;
MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, &*Target);
@ -199,7 +181,7 @@ TEST_F(SourceManagerTest, getMacroArgExpandedLocation) {
HeaderBuf->getBufferSize(), 0);
SourceMgr.overrideFileContents(headerFile, std::move(HeaderBuf));
VoidModuleLoader ModLoader;
TrivialModuleLoader ModLoader;
MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, &*Target);
@ -318,7 +300,7 @@ TEST_F(SourceManagerTest, isBeforeInTranslationUnitWithMacroInInclude) {
HeaderBuf->getBufferSize(), 0);
SourceMgr.overrideFileContents(headerFile, std::move(HeaderBuf));
VoidModuleLoader ModLoader;
TrivialModuleLoader ModLoader;
MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, &*Target);

View File

@ -27,24 +27,6 @@ using namespace clang;
namespace {
class VoidModuleLoader : public ModuleLoader {
ModuleLoadResult loadModule(SourceLocation ImportLoc,
ModuleIdPath Path,
Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) override {
return ModuleLoadResult();
}
void makeModuleVisible(Module *Mod,
Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc) override { }
GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override
{ return nullptr; }
bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override
{ return 0; }
};
// The test fixture.
class LexerTest : public ::testing::Test {
protected:
@ -64,7 +46,7 @@ protected:
llvm::MemoryBuffer::getMemBuffer(Source);
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
VoidModuleLoader ModLoader;
TrivialModuleLoader ModLoader;
MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, Target.get());

View File

@ -32,25 +32,6 @@ using namespace clang;
namespace {
// Stub out module loading.
class VoidModuleLoader : public ModuleLoader {
ModuleLoadResult loadModule(SourceLocation ImportLoc,
ModuleIdPath Path,
Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) override {
return ModuleLoadResult();
}
void makeModuleVisible(Module *Mod,
Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc) override { }
GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override
{ return nullptr; }
bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override
{ return 0; }
};
// Stub to collect data from InclusionDirective callbacks.
class InclusionDirectiveCallbacks : public PPCallbacks {
public:
@ -161,7 +142,7 @@ protected:
llvm::MemoryBuffer::getMemBuffer(SourceText);
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
VoidModuleLoader ModLoader;
TrivialModuleLoader ModLoader;
MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
@ -199,7 +180,7 @@ protected:
llvm::MemoryBuffer::getMemBuffer(SourceText, "test.cl");
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(SourceBuf)));
VoidModuleLoader ModLoader;
TrivialModuleLoader ModLoader;
MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, OpenCLLangOpts, Target.get());

View File

@ -51,24 +51,6 @@ protected:
IntrusiveRefCntPtr<TargetInfo> Target;
};
class VoidModuleLoader : public ModuleLoader {
ModuleLoadResult loadModule(SourceLocation ImportLoc,
ModuleIdPath Path,
Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) override {
return ModuleLoadResult();
}
void makeModuleVisible(Module *Mod,
Module::NameVisibilityKind Visibility,
SourceLocation ImportLoc) override { }
GlobalModuleIndex *loadGlobalModuleIndex(SourceLocation TriggerLoc) override
{ return nullptr; }
bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override
{ return 0; }
};
TEST_F(PPConditionalDirectiveRecordTest, PPRecAPI) {
const char *source =
"0 1\n"
@ -93,7 +75,7 @@ TEST_F(PPConditionalDirectiveRecordTest, PPRecAPI) {
llvm::MemoryBuffer::getMemBuffer(source);
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
VoidModuleLoader ModLoader;
TrivialModuleLoader ModLoader;
MemoryBufferCache PCMCache;
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
Diags, LangOpts, Target.get());