From 8128f3327f13fa2e51bf090435c2bd3d7d14dea2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 5 May 2017 22:18:51 +0000 Subject: [PATCH] Add support for building modules from preprocessed source. To support this, an optional marker "#pragma clang module contents" is recognized in module map files, and the rest of the module map file from that point onwards is treated as the source of the module. Preprocessing a module map produces the input module followed by the marker and then the preprocessed contents of the module. Ignoring line markers, a preprocessed module might look like this: module A { header "a.h" } #pragma clang module contents #pragma clang module begin A // ... a.h ... #pragma clang module end The preprocessed output generates line markers, which are not accepted by the module map parser, so -x c++-module-map-cpp-output should be used to compile such outputs. A couple of major parts do not work yet: 1) The files that are listed in the module map must exist on disk, in order to build the on-disk header -> module lookup table in the PCM file. To fix this, we need the preprocessed output to track the file size and other stat information we might use to build the lookup table. 2) Declaration ownership semantics don't work properly yet, since mapping from a source location to a module relies on mapping from FileIDs to modules, which we can't do if module transitions can occur in the middle of a file. llvm-svn: 302309 --- clang/include/clang/Frontend/FrontendAction.h | 2 + clang/include/clang/Lex/HeaderSearch.h | 12 +- clang/include/clang/Lex/ModuleMap.h | 10 +- clang/lib/Frontend/FrontendAction.cpp | 227 ++++++++++++++++--------- clang/lib/Frontend/FrontendActions.cpp | 12 ++ clang/lib/Frontend/Rewrite/FrontendActions.cpp | 12 ++ clang/lib/Lex/HeaderSearch.cpp | 10 +- clang/lib/Lex/ModuleMap.cpp | 58 ++++++- clang/lib/Sema/SemaDecl.cpp | 19 ++- clang/test/Modules/preprocess-module.cpp | 40 ++++- 10 files changed, 296 insertions(+), 106 deletions(-) diff --git a/clang/include/clang/Frontend/FrontendAction.h b/clang/include/clang/Frontend/FrontendAction.h index 384499a..8d690a4 100644 --- a/clang/include/clang/Frontend/FrontendAction.h +++ b/clang/include/clang/Frontend/FrontendAction.h @@ -146,6 +146,8 @@ public: return *CurrentASTUnit; } + Module *getCurrentModule() const; + std::unique_ptr takeCurrentASTUnit() { return std::move(CurrentASTUnit); } diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h index 64c6f2a..6e24e18 100644 --- a/clang/include/clang/Lex/HeaderSearch.h +++ b/clang/include/clang/Lex/HeaderSearch.h @@ -538,9 +538,15 @@ public: /// /// \param File The module map file. /// \param IsSystem Whether this file is in a system header directory. + /// \param ID If the module map file is already mapped (perhaps as part of + /// processing a preprocessed module), the ID of the file. + /// \param Offset [inout] An offset within ID to start parsing. On exit, + /// filled by the end of the parsed contents (either EOF or the + /// location of an end-of-module-map pragma). /// /// \returns true if an error occurred, false otherwise. - bool loadModuleMapFile(const FileEntry *File, bool IsSystem); + bool loadModuleMapFile(const FileEntry *File, bool IsSystem, + FileID ID = FileID(), unsigned *Offset = nullptr); /// \brief Collect the set of all known, top-level modules. /// @@ -686,7 +692,9 @@ private: LoadModuleMapResult loadModuleMapFileImpl(const FileEntry *File, bool IsSystem, - const DirectoryEntry *Dir); + const DirectoryEntry *Dir, + FileID ID = FileID(), + unsigned *Offset = nullptr); /// \brief Try to load the module map file in the given directory. /// diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h index 4613672..0fd6abe 100644 --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -546,14 +546,20 @@ public: /// \param HomeDir The directory in which relative paths within this module /// map file will be resolved. /// + /// \param ID The FileID of the file to process, if we've already entered it. + /// + /// \param Offset [inout] On input the offset at which to start parsing. On + /// output, the offset at which the module map terminated. + /// /// \param ExternModuleLoc The location of the "extern module" declaration /// that caused us to load this module map file, if any. /// /// \returns true if an error occurred, false otherwise. bool parseModuleMapFile(const FileEntry *File, bool IsSystem, - const DirectoryEntry *HomeDir, + const DirectoryEntry *HomeDir, FileID ID = FileID(), + unsigned *Offset = nullptr, SourceLocation ExternModuleLoc = SourceLocation()); - + /// \brief Dump the contents of the module map, for debugging purposes. void dump(); diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp index d26b693..1fbb2b0 100644 --- a/clang/lib/Frontend/FrontendAction.cpp +++ b/clang/lib/Frontend/FrontendAction.cpp @@ -136,6 +136,12 @@ void FrontendAction::setCurrentInput(const FrontendInputFile &CurrentInput, CurrentASTUnit = std::move(AST); } +Module *FrontendAction::getCurrentModule() const { + CompilerInstance &CI = getCompilerInstance(); + return CI.getPreprocessor().getHeaderSearchInfo().lookupModule( + CI.getLangOpts().CurrentModule, /*AllowSearch*/false); +} + std::unique_ptr FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI, StringRef InFile) { @@ -188,16 +194,25 @@ FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI, return llvm::make_unique(std::move(Consumers)); } -// For preprocessed files, if the first line is the linemarker and specifies -// the original source file name, use that name as the input file name. -static bool ReadOriginalFileName(CompilerInstance &CI, std::string &InputFile) -{ - bool Invalid = false; +/// For preprocessed files, if the first line is the linemarker and specifies +/// the original source file name, use that name as the input file name. +/// Returns the location of the first token after the line marker directive. +/// +/// \param CI The compiler instance. +/// \param InputFile Populated with the filename from the line marker. +/// \param AddLineNote If \c true, add a line note corresponding to this line +/// directive. Only use this if the directive will not actually be +/// visited by the preprocessor. +static SourceLocation ReadOriginalFileName(CompilerInstance &CI, + std::string &InputFile, + bool AddLineNote = false) { auto &SourceMgr = CI.getSourceManager(); auto MainFileID = SourceMgr.getMainFileID(); + + bool Invalid = false; const auto *MainFileBuf = SourceMgr.getBuffer(MainFileID, &Invalid); if (Invalid) - return false; + return SourceLocation(); std::unique_ptr RawLexer( new Lexer(MainFileID, MainFileBuf, SourceMgr, CI.getLangOpts())); @@ -209,19 +224,37 @@ static bool ReadOriginalFileName(CompilerInstance &CI, std::string &InputFile) // we use FILENAME as the input file name. Token T; if (RawLexer->LexFromRawLexer(T) || T.getKind() != tok::hash) - return false; + return SourceLocation(); if (RawLexer->LexFromRawLexer(T) || T.isAtStartOfLine() || T.getKind() != tok::numeric_constant) - return false; + return SourceLocation(); + + unsigned LineNo; + SourceLocation LineNoLoc = T.getLocation(); + if (AddLineNote) { + llvm::SmallString<16> Buffer; + if (Lexer::getSpelling(LineNoLoc, Buffer, SourceMgr, CI.getLangOpts()) + .getAsInteger(10, LineNo)) + return SourceLocation(); + } + RawLexer->LexFromRawLexer(T); if (T.isAtStartOfLine() || T.getKind() != tok::string_literal) - return false; + return SourceLocation(); StringLiteralParser Literal(T, CI.getPreprocessor()); if (Literal.hadError) - return false; + return SourceLocation(); + RawLexer->LexFromRawLexer(T); + if (T.isNot(tok::eof) && !T.isAtStartOfLine()) + return SourceLocation(); InputFile = Literal.GetString().str(); - return true; + + if (AddLineNote) + CI.getSourceManager().AddLineNote( + LineNoLoc, LineNo, SourceMgr.getLineTableFilenameID(InputFile)); + + return T.getLocation(); } static SmallVectorImpl & @@ -339,42 +372,44 @@ collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr, return std::error_code(); } -/// Parse a module map and compute the corresponding real input buffer that -/// should be used to build the module described by that module map and the -/// current module name. -static std::unique_ptr -getInputBufferForModuleMap(CompilerInstance &CI, StringRef Filename, - bool IsSystem) { - // Find the module map file. - const FileEntry *ModuleMap = - CI.getFileManager().getFile(Filename, /*openFile*/true); - if (!ModuleMap) { - CI.getDiagnostics().Report(diag::err_module_map_not_found) - << Filename; - return nullptr; - } +static bool +loadModuleMapForModuleBuild(CompilerInstance &CI, StringRef Filename, + bool IsSystem, bool IsPreprocessed, + unsigned &Offset) { + auto &SrcMgr = CI.getSourceManager(); + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); - // Find the module map file from which it was generated, if different. - const FileEntry *OriginalModuleMap = ModuleMap; - StringRef OriginalModuleMapName = CI.getFrontendOpts().OriginalModuleMap; - if (!OriginalModuleMapName.empty()) { - OriginalModuleMap = CI.getFileManager().getFile(OriginalModuleMapName, - /*openFile*/ true); - if (!OriginalModuleMap) { - CI.getDiagnostics().Report(diag::err_module_map_not_found) - << OriginalModuleMapName; - return nullptr; - } + // Map the current input to a file. + FileID ModuleMapID = SrcMgr.getMainFileID(); + const FileEntry *ModuleMap = SrcMgr.getFileEntryForID(ModuleMapID); + + // If the module map is preprocessed, handle the initial line marker; + // line directives are not part of the module map syntax in general. + Offset = 0; + if (IsPreprocessed) { + std::string PresumedModuleMapFile; + SourceLocation EndOfLineMarker = + ReadOriginalFileName(CI, PresumedModuleMapFile, /*AddLineNote*/true); + if (EndOfLineMarker.isValid()) + Offset = CI.getSourceManager().getDecomposedLoc(EndOfLineMarker).second; + // FIXME: Use PresumedModuleMapFile as the MODULE_MAP_FILE in the PCM. } - - // Parse the module map file. - HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); - if (HS.loadModuleMapFile(ModuleMap, IsSystem)) - return nullptr; - + + // Load the module map file. + if (HS.loadModuleMapFile(ModuleMap, IsSystem, ModuleMapID, &Offset)) + return true; + + if (SrcMgr.getBuffer(ModuleMapID)->getBufferSize() == Offset) + Offset = 0; + + return false; +} + +static Module *prepareToBuildModule(CompilerInstance &CI, + StringRef ModuleMapFilename) { if (CI.getLangOpts().CurrentModule.empty()) { CI.getDiagnostics().Report(diag::err_missing_module_name); - + // FIXME: Eventually, we could consider asking whether there was just // a single module described in the module map, and use that as a // default. Then it would be fairly trivial to just "compile" a module @@ -382,21 +417,14 @@ getInputBufferForModuleMap(CompilerInstance &CI, StringRef Filename, return nullptr; } - // If we're being run from the command-line, the module build stack will not - // have been filled in yet, so complete it now in order to allow us to detect - // module cycles. - SourceManager &SourceMgr = CI.getSourceManager(); - if (SourceMgr.getModuleBuildStack().empty()) - SourceMgr.pushModuleBuildStack(CI.getLangOpts().CurrentModule, - FullSourceLoc(SourceLocation(), SourceMgr)); - // Dig out the module definition. + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); Module *M = HS.lookupModule(CI.getLangOpts().CurrentModule, /*AllowSearch=*/false); if (!M) { CI.getDiagnostics().Report(diag::err_missing_module) - << CI.getLangOpts().CurrentModule << Filename; - + << CI.getLangOpts().CurrentModule << ModuleMapFilename; + return nullptr; } @@ -417,11 +445,45 @@ getInputBufferForModuleMap(CompilerInstance &CI, StringRef Filename, return nullptr; } - if (OriginalModuleMap != ModuleMap) { - M->IsInferred = true; - HS.getModuleMap().setInferredModuleAllowedBy(M, OriginalModuleMap); + // Inform the preprocessor that includes from within the input buffer should + // be resolved relative to the build directory of the module map file. + CI.getPreprocessor().setMainFileDir(M->Directory); + + // If the module was inferred from a different module map (via an expanded + // umbrella module definition), track that fact. + // FIXME: It would be preferable to fill this in as part of processing + // the module map, rather than adding it after the fact. + StringRef OriginalModuleMapName = CI.getFrontendOpts().OriginalModuleMap; + if (!OriginalModuleMapName.empty()) { + auto *OriginalModuleMap = + CI.getFileManager().getFile(OriginalModuleMapName, + /*openFile*/ true); + if (!OriginalModuleMap) { + CI.getDiagnostics().Report(diag::err_module_map_not_found) + << OriginalModuleMapName; + return nullptr; + } + if (OriginalModuleMap != CI.getSourceManager().getFileEntryForID( + CI.getSourceManager().getMainFileID())) { + M->IsInferred = true; + CI.getPreprocessor().getHeaderSearchInfo().getModuleMap() + .setInferredModuleAllowedBy(M, OriginalModuleMap); + } } + // If we're being run from the command-line, the module build stack will not + // have been filled in yet, so complete it now in order to allow us to detect + // module cycles. + SourceManager &SourceMgr = CI.getSourceManager(); + if (SourceMgr.getModuleBuildStack().empty()) + SourceMgr.pushModuleBuildStack(CI.getLangOpts().CurrentModule, + FullSourceLoc(SourceLocation(), SourceMgr)); + return M; +} + +/// Compute the input buffer that should be used to build the specified module. +static std::unique_ptr +getInputBufferForModule(CompilerInstance &CI, Module *M) { FileManager &FileMgr = CI.getFileManager(); // Collect the set of #includes we need to build the module. @@ -441,10 +503,6 @@ getInputBufferForModuleMap(CompilerInstance &CI, StringRef Filename, return nullptr; } - // Inform the preprocessor that includes from within the input buffer should - // be resolved relative to the build directory of the module map file. - CI.getPreprocessor().setMainFileDir(M->Directory); - return llvm::MemoryBuffer::getMemBufferCopy( HeaderContents, Module::getModuleInputBufferName()); } @@ -457,7 +515,6 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, setCompilerInstance(&CI); StringRef InputFile = Input.getFile(); - FrontendInputFile FileToProcess = Input; bool HasBegunSourceFile = false; if (!BeginInvocation(CI)) goto failure; @@ -597,36 +654,45 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, &CI.getPreprocessor()); HasBegunSourceFile = true; + // Initialize the main file entry. + if (!CI.InitializeSourceManager(Input)) + goto failure; + // For module map files, we first parse the module map and synthesize a // "" buffer before more conventional processing. if (Input.getKind().getFormat() == InputKind::ModuleMap) { CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleMap); - auto Buffer = getInputBufferForModuleMap(CI, InputFile, Input.isSystem()); - if (!Buffer) + unsigned OffsetToContents; + if (loadModuleMapForModuleBuild(CI, Input.getFile(), Input.isSystem(), + Input.isPreprocessed(), OffsetToContents)) goto failure; - Module *CurrentModule = - CI.getPreprocessor().getHeaderSearchInfo().lookupModule( - CI.getLangOpts().CurrentModule, - /*AllowSearch=*/false); - assert(CurrentModule && "no module info for current module"); + auto *CurrentModule = prepareToBuildModule(CI, Input.getFile()); + if (!CurrentModule) + goto failure; + + if (OffsetToContents) + // If the module contents are in the same file, skip to them. + CI.getPreprocessor().setSkipMainFilePreamble(OffsetToContents, true); + else { + // Otherwise, convert the module description to a suitable input buffer. + auto Buffer = getInputBufferForModule(CI, CurrentModule); + if (!Buffer) + goto failure; - // The input that we end up processing is the generated buffer, not the - // module map file itself. - FileToProcess = FrontendInputFile( - Buffer.release(), Input.getKind().withFormat(InputKind::Source), - CurrentModule->IsSystem); + // Reinitialize the main file entry to refer to the new input. + if (!CI.InitializeSourceManager(FrontendInputFile( + Buffer.release(), Input.getKind().withFormat(InputKind::Source), + CurrentModule->IsSystem))) + goto failure; + } } // Initialize the action. if (!BeginSourceFileAction(CI, InputFile)) goto failure; - // Initialize the main file entry. - if (!CI.InitializeSourceManager(FileToProcess)) - goto failure; - // Create the AST context and consumer unless this is a preprocessor only // action. if (!usesPreprocessorOnly()) { @@ -636,13 +702,12 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, // For preprocessed files, check if the first line specifies the original // source file name with a linemarker. - std::string OrigFile; + std::string PresumedInputFile = InputFile; if (Input.isPreprocessed()) - if (ReadOriginalFileName(CI, OrigFile)) - InputFile = OrigFile; + ReadOriginalFileName(CI, PresumedInputFile); std::unique_ptr Consumer = - CreateWrappedASTConsumer(CI, InputFile); + CreateWrappedASTConsumer(CI, PresumedInputFile); if (!Consumer) goto failure; diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index dd7c12f..baaf93b 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -542,6 +542,18 @@ void PrintPreprocessedAction::ExecuteAction() { CI.createDefaultOutputFile(BinaryMode, getCurrentFile()); if (!OS) return; + // If we're preprocessing a module map, start by dumping the contents of the + // module itself before switching to the input buffer. + auto &Input = getCurrentInput(); + if (Input.getKind().getFormat() == InputKind::ModuleMap) { + if (Input.isFile()) + (*OS) << "# 1 \"" << Input.getFile() << "\"\n"; + // FIXME: Include additional information here so that we don't need the + // original source files to exist on disk. + getCurrentModule()->print(*OS); + (*OS) << "#pragma clang module contents\n"; + } + DoPrintPreprocessedInput(CI.getPreprocessor(), OS.get(), CI.getPreprocessorOutputOpts()); } diff --git a/clang/lib/Frontend/Rewrite/FrontendActions.cpp b/clang/lib/Frontend/Rewrite/FrontendActions.cpp index 2e76e2e..8c5eb16 100644 --- a/clang/lib/Frontend/Rewrite/FrontendActions.cpp +++ b/clang/lib/Frontend/Rewrite/FrontendActions.cpp @@ -196,6 +196,18 @@ void RewriteIncludesAction::ExecuteAction() { CI.createDefaultOutputFile(true, getCurrentFile()); if (!OS) return; + // If we're preprocessing a module map, start by dumping the contents of the + // module itself before switching to the input buffer. + auto &Input = getCurrentInput(); + if (Input.getKind().getFormat() == InputKind::ModuleMap) { + if (Input.isFile()) + (*OS) << "# 1 \"" << Input.getFile() << "\"\n"; + // FIXME: Include additional information here so that we don't need the + // original source files to exist on disk. + getCurrentModule()->print(*OS); + (*OS) << "#pragma clang module contents\n"; + } + RewriteIncludesInInput(CI.getPreprocessor(), OS.get(), CI.getPreprocessorOutputOpts()); } diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp index bd425a0..f5b7c59 100644 --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -1325,7 +1325,8 @@ static const FileEntry *getPrivateModuleMap(const FileEntry *File, return FileMgr.getFile(PrivateFilename); } -bool HeaderSearch::loadModuleMapFile(const FileEntry *File, bool IsSystem) { +bool HeaderSearch::loadModuleMapFile(const FileEntry *File, bool IsSystem, + FileID ID, unsigned *Offset) { // Find the directory for the module. For frameworks, that may require going // up from the 'Modules' directory. const DirectoryEntry *Dir = nullptr; @@ -1344,7 +1345,7 @@ bool HeaderSearch::loadModuleMapFile(const FileEntry *File, bool IsSystem) { } } - switch (loadModuleMapFileImpl(File, IsSystem, Dir)) { + switch (loadModuleMapFileImpl(File, IsSystem, Dir, ID, Offset)) { case LMM_AlreadyLoaded: case LMM_NewlyLoaded: return false; @@ -1357,7 +1358,8 @@ bool HeaderSearch::loadModuleMapFile(const FileEntry *File, bool IsSystem) { HeaderSearch::LoadModuleMapResult HeaderSearch::loadModuleMapFileImpl(const FileEntry *File, bool IsSystem, - const DirectoryEntry *Dir) { + const DirectoryEntry *Dir, FileID ID, + unsigned *Offset) { assert(File && "expected FileEntry"); // Check whether we've already loaded this module map, and mark it as being @@ -1366,7 +1368,7 @@ HeaderSearch::loadModuleMapFileImpl(const FileEntry *File, bool IsSystem, if (!AddResult.second) return AddResult.first->second ? LMM_AlreadyLoaded : LMM_InvalidModuleMap; - if (ModMap.parseModuleMapFile(File, IsSystem, Dir)) { + if (ModMap.parseModuleMapFile(File, IsSystem, Dir, ID, Offset)) { LoadedModuleMaps[File] = false; return LMM_InvalidModuleMap; } diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp index 512d7dc..70d37d3 100644 --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -1132,14 +1132,17 @@ namespace clang { } bool parseModuleMapFile(); + + bool terminatedByDirective() { return false; } + SourceLocation getLocation() { return Tok.getLocation(); } }; } SourceLocation ModuleMapParser::consumeToken() { -retry: SourceLocation Result = Tok.getLocation(); + +retry: Tok.clear(); - Token LToken; L.LexFromRawLexer(LToken); Tok.Location = LToken.getLocation().getRawEncoding(); @@ -1232,9 +1235,28 @@ retry: case tok::comment: goto retry; - + + case tok::hash: + // A module map can be terminated prematurely by + // #pragma clang module contents + // When building the module, we'll treat the rest of the file as the + // contents of the module. + { + auto NextIsIdent = [&](StringRef Str) -> bool { + L.LexFromRawLexer(LToken); + return !LToken.isAtStartOfLine() && LToken.is(tok::raw_identifier) && + LToken.getRawIdentifier() == Str; + }; + if (NextIsIdent("pragma") && NextIsIdent("clang") && + NextIsIdent("module") && NextIsIdent("contents")) { + Tok.Kind = MMToken::EndOfFile; + break; + } + } + LLVM_FALLTHROUGH; + default: - Diags.Report(LToken.getLocation(), diag::err_mmap_unknown_token); + Diags.Report(Tok.getLocation(), diag::err_mmap_unknown_token); HadError = true; goto retry; } @@ -1682,7 +1704,8 @@ void ModuleMapParser::parseExternModuleDecl() { File, /*IsSystem=*/false, Map.HeaderInfo.getHeaderSearchOpts().ModuleMapFileHomeIsCwd ? Directory - : File->getDir(), ExternLoc); + : File->getDir(), + FileID(), nullptr, ExternLoc); } /// Whether to add the requirement \p Feature to the module \p M. @@ -2522,28 +2545,45 @@ bool ModuleMapParser::parseModuleMapFile() { } bool ModuleMap::parseModuleMapFile(const FileEntry *File, bool IsSystem, - const DirectoryEntry *Dir, + const DirectoryEntry *Dir, FileID ID, + unsigned *Offset, SourceLocation ExternModuleLoc) { + assert(Target && "Missing target information"); llvm::DenseMap::iterator Known = ParsedModuleMap.find(File); if (Known != ParsedModuleMap.end()) return Known->second; + // If the module map file wasn't already entered, do so now. + if (ID.isInvalid()) { + auto FileCharacter = IsSystem ? SrcMgr::C_System : SrcMgr::C_User; + ID = SourceMgr.createFileID(File, ExternModuleLoc, FileCharacter); + } + assert(Target && "Missing target information"); - auto FileCharacter = IsSystem ? SrcMgr::C_System : SrcMgr::C_User; - FileID ID = SourceMgr.createFileID(File, ExternModuleLoc, FileCharacter); const llvm::MemoryBuffer *Buffer = SourceMgr.getBuffer(ID); if (!Buffer) return ParsedModuleMap[File] = true; + assert((!Offset || *Offset <= Buffer->getBufferSize()) && + "invalid buffer offset"); // Parse this module map file. - Lexer L(ID, SourceMgr.getBuffer(ID), SourceMgr, MMapLangOpts); + Lexer L(SourceMgr.getLocForStartOfFile(ID), MMapLangOpts, + Buffer->getBufferStart(), + Buffer->getBufferStart() + (Offset ? *Offset : 0), + Buffer->getBufferEnd()); SourceLocation Start = L.getSourceLocation(); ModuleMapParser Parser(L, SourceMgr, Target, Diags, *this, File, Dir, BuiltinIncludeDir, IsSystem); bool Result = Parser.parseModuleMapFile(); ParsedModuleMap[File] = Result; + if (Offset) { + auto Loc = SourceMgr.getDecomposedLoc(Parser.getLocation()); + assert(Loc.first == ID && "stopped in a different file?"); + *Offset = Loc.second; + } + // Notify callbacks that we parsed it. for (const auto &Cb : Callbacks) Cb->moduleMapFileRead(Start, *File, IsSystem); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d4c0783..3b83165 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15902,7 +15902,7 @@ void Sema::ActOnModuleBegin(SourceLocation DirectiveLoc, Module *Mod) { VisibleModules.setVisible(Mod, DirectiveLoc); } -void Sema::ActOnModuleEnd(SourceLocation EofLoc, Module *Mod) { +void Sema::ActOnModuleEnd(SourceLocation EomLoc, Module *Mod) { if (getLangOpts().ModulesLocalVisibility) { VisibleModules = std::move(ModuleScopes.back().OuterVisibleModules); // Leaving a module hides namespace names, so our visible namespace cache @@ -15914,12 +15914,19 @@ void Sema::ActOnModuleEnd(SourceLocation EofLoc, Module *Mod) { "left the wrong module scope"); ModuleScopes.pop_back(); - // We got to the end of processing a #include of a local module. Create an + // We got to the end of processing a local module. Create an // ImportDecl as we would for an imported module. - FileID File = getSourceManager().getFileID(EofLoc); - assert(File != getSourceManager().getMainFileID() && - "end of submodule in main source file"); - SourceLocation DirectiveLoc = getSourceManager().getIncludeLoc(File); + FileID File = getSourceManager().getFileID(EomLoc); + SourceLocation DirectiveLoc; + if (EomLoc == getSourceManager().getLocForEndOfFile(File)) { + // We reached the end of a #included module header. Use the #include loc. + assert(File != getSourceManager().getMainFileID() && + "end of submodule in main source file"); + DirectiveLoc = getSourceManager().getIncludeLoc(File); + } else { + // We reached an EOM pragma. Use the pragma location. + DirectiveLoc = EomLoc; + } BuildModuleInclude(DirectiveLoc, Mod); } diff --git a/clang/test/Modules/preprocess-module.cpp b/clang/test/Modules/preprocess-module.cpp index 337cafb..a3b7892 100644 --- a/clang/test/Modules/preprocess-module.cpp +++ b/clang/test/Modules/preprocess-module.cpp @@ -1,10 +1,35 @@ // RUN: rm -rf %t +// RUN: mkdir %t // RUN: not %clang_cc1 -fmodules -fmodule-name=file -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E 2>&1 | FileCheck %s --check-prefix=MISSING-FWD // MISSING-FWD: module 'fwd' is needed -// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodules-cache-path=%t -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E | FileCheck %s --check-prefix=CHECK --check-prefix=NO-REWRITE -// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodules-cache-path=%t -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E -frewrite-includes | FileCheck %s --check-prefix=CHECK --check-prefix=REWRITE +// RUN: %clang_cc1 -fmodules -fmodule-name=fwd -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -emit-module -o %t/fwd.pcm + +// Check that we can preprocess modules, and get the expected output. +// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E -o %t/no-rewrite.ii +// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm -I%S/Inputs/preprocess -x c++-module-map %S/Inputs/preprocess/module.modulemap -E -frewrite-includes -o %t/rewrite.ii +// +// RUN: FileCheck %s --input-file %t/no-rewrite.ii --check-prefix=CHECK --check-prefix=NO-REWRITE +// RUN: FileCheck %s --input-file %t/rewrite.ii --check-prefix=CHECK --check-prefix=REWRITE + +// Check that we can build a module from the preprocessed output. +// FIXME: For now, we need the headers to exist. +// RUN: touch %t/file.h %t/file2.h +// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm -x c++-module-map-cpp-output %t/no-rewrite.ii -emit-module -o %t/no-rewrite.pcm +// RUN: %clang_cc1 -fmodules -fmodule-name=file -fmodule-file=%t/fwd.pcm -x c++-module-map-cpp-output %t/rewrite.ii -emit-module -o %t/rewrite.pcm + +// Check the module we built works. +// RUN: %clang_cc1 -fmodules -fmodule-file=%t/no-rewrite.pcm %s -verify +// RUN: %clang_cc1 -fmodules -fmodule-file=%t/rewrite.pcm %s -verify + + +// == module map +// CHECK: # 1 "{{.*}}module.modulemap" +// CHECK: module file { +// CHECK: header "file.h" +// CHECK: header "file2.h" +// CHECK: } // == file.h // CHECK: # 1 "" @@ -63,3 +88,14 @@ // REWRITE: #pragma clang module end // CHECK: # 3 "" 2 // NO-REWRITE: #pragma clang module end + + +// expected-no-diagnostics + +// FIXME: This should be rejected: we have not imported the submodule defining it yet. +__FILE *a; + +#pragma clang module import file + +FILE *b; +int x = file2; -- 2.7.4