forked from OSchip/llvm-project
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:
parent
01a48b5dfd
commit
5d2ed48987
|
@ -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<
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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'}}
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue