Author: rsmith Date: Fri Jun 9 14:22:32 2017 New Revision: 305101 URL: http://llvm.org/viewvc/llvm-project?rev=305101&view=rev Log: 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. Added: cfe/trunk/test/Modules/preprocess-build.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td cfe/trunk/include/clang/Frontend/ASTUnit.h cfe/trunk/include/clang/Frontend/CompilerInstance.h cfe/trunk/include/clang/Lex/ModuleLoader.h cfe/trunk/include/clang/Lex/Preprocessor.h cfe/trunk/lib/Frontend/ASTUnit.cpp cfe/trunk/lib/Frontend/CompilerInstance.cpp cfe/trunk/lib/Lex/Pragma.cpp cfe/trunk/unittests/Basic/SourceManagerTest.cpp cfe/trunk/unittests/Lex/LexerTest.cpp cfe/trunk/unittests/Lex/PPCallbacksTest.cpp cfe/trunk/unittests/Lex/PPConditionalDirectiveRecordTest.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td Fri Jun 9 14:22:32 2017 @@ -527,6 +527,10 @@ def err_pp_module_end_without_module_beg "'#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< Modified: cfe/trunk/include/clang/Frontend/ASTUnit.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/ASTUnit.h?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/include/clang/Frontend/ASTUnit.h (original) +++ cfe/trunk/include/clang/Frontend/ASTUnit.h Fri Jun 9 14:22:32 2017 @@ -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 Modified: cfe/trunk/include/clang/Frontend/CompilerInstance.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CompilerInstance.h?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/include/clang/Frontend/CompilerInstance.h (original) +++ cfe/trunk/include/clang/Frontend/CompilerInstance.h Fri Jun 9 14:22:32 2017 @@ -136,6 +136,10 @@ class CompilerInstance : public ModuleLo /// 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; Modified: cfe/trunk/include/clang/Lex/ModuleLoader.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/ModuleLoader.h?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/include/clang/Lex/ModuleLoader.h (original) +++ cfe/trunk/include/clang/Lex/ModuleLoader.h Fri Jun 9 14:22:32 2017 @@ -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; + } +}; } Modified: cfe/trunk/include/clang/Lex/Preprocessor.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/Preprocessor.h?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/include/clang/Lex/Preprocessor.h (original) +++ cfe/trunk/include/clang/Lex/Preprocessor.h Fri Jun 9 14:22:32 2017 @@ -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 Modified: cfe/trunk/lib/Frontend/ASTUnit.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/ASTUnit.cpp?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/lib/Frontend/ASTUnit.cpp (original) +++ cfe/trunk/lib/Frontend/ASTUnit.cpp Fri Jun 9 14:22:32 2017 @@ -716,7 +716,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFr 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; Modified: cfe/trunk/lib/Frontend/CompilerInstance.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInstance.cpp?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/lib/Frontend/CompilerInstance.cpp (original) +++ cfe/trunk/lib/Frontend/CompilerInstance.cpp Fri Jun 9 14:22:32 2017 @@ -667,6 +667,8 @@ void CompilerInstance::clearOutputFiles( 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 getLanguageFr /// \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(CompilerIn 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(CompilerIn 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(CompilerIn 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(CompilerIn 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(CompilerIn }, 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(CompilerIn // 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(SourceLocat 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(SourceLocat *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(SourceLocat 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(SourceLocat } 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(SourceLocat // 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(SourceLocat 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) { Modified: cfe/trunk/lib/Lex/Pragma.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/Pragma.cpp?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/lib/Lex/Pragma.cpp (original) +++ cfe/trunk/lib/Lex/Pragma.cpp Fri Jun 9 14:22:32 2017 @@ -754,6 +754,88 @@ void Preprocessor::HandlePragmaIncludeAl 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 P } }; +/// 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::RegisterBuiltinPragma 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()); Added: cfe/trunk/test/Modules/preprocess-build.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/preprocess-build.cpp?rev=305101&view=auto ============================================================================== --- cfe/trunk/test/Modules/preprocess-build.cpp (added) +++ cfe/trunk/test/Modules/preprocess-build.cpp Fri Jun 9 14:22:32 2017 @@ -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'}} Modified: cfe/trunk/unittests/Basic/SourceManagerTest.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Basic/SourceManagerTest.cpp?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/unittests/Basic/SourceManagerTest.cpp (original) +++ cfe/trunk/unittests/Basic/SourceManagerTest.cpp Fri Jun 9 14:22:32 2017 @@ -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, isBeforeInTran 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, getMacroArgExp 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, isBeforeInTran 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); Modified: cfe/trunk/unittests/Lex/LexerTest.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Lex/LexerTest.cpp?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/unittests/Lex/LexerTest.cpp (original) +++ cfe/trunk/unittests/Lex/LexerTest.cpp Fri Jun 9 14:22:32 2017 @@ -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()); Modified: cfe/trunk/unittests/Lex/PPCallbacksTest.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Lex/PPCallbacksTest.cpp?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/unittests/Lex/PPCallbacksTest.cpp (original) +++ cfe/trunk/unittests/Lex/PPCallbacksTest.cpp Fri Jun 9 14:22:32 2017 @@ -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()); Modified: cfe/trunk/unittests/Lex/PPConditionalDirectiveRecordTest.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Lex/PPConditionalDirectiveRecordTest.cpp?rev=305101&r1=305100&r2=305101&view=diff ============================================================================== --- cfe/trunk/unittests/Lex/PPConditionalDirectiveRecordTest.cpp (original) +++ cfe/trunk/unittests/Lex/PPConditionalDirectiveRecordTest.cpp Fri Jun 9 14:22:32 2017 @@ -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, 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()); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits