diff --git a/clang/include/clang/Lex/ExternalPreprocessorSource.h b/clang/include/clang/Lex/ExternalPreprocessorSource.h --- a/clang/include/clang/Lex/ExternalPreprocessorSource.h +++ b/clang/include/clang/Lex/ExternalPreprocessorSource.h @@ -13,6 +13,8 @@ #ifndef LLVM_CLANG_LEX_EXTERNALPREPROCESSORSOURCE_H #define LLVM_CLANG_LEX_EXTERNALPREPROCESSORSOURCE_H +#include "clang/Lex/Preprocessor.h" + namespace clang { class IdentifierInfo; @@ -40,6 +42,9 @@ /// Map a module ID to a module. virtual Module *getModule(unsigned ModuleID) = 0; + + /// Return the set of files directly included in the given (sub)module. + virtual const Preprocessor::IncludedFilesSet *getIncludedFiles(Module *M) = 0; }; } diff --git a/clang/include/clang/Lex/HeaderSearch.h b/clang/include/clang/Lex/HeaderSearch.h --- a/clang/include/clang/Lex/HeaderSearch.h +++ b/clang/include/clang/Lex/HeaderSearch.h @@ -51,6 +51,8 @@ /// The preprocessor keeps track of this information for each /// file that is \#included. struct HeaderFileInfo { + // TODO: Whether the file was imported is not a property of the file itself. + // It's a preprocessor state, move it there. /// True if this is a \#import'd file. unsigned isImport : 1; @@ -89,9 +91,6 @@ /// Whether this file has been looked up as a header. unsigned IsValid : 1; - /// The number of times the file has been included already. - unsigned short NumIncludes = 0; - /// The ID number of the controlling macro. /// /// This ID number will be non-zero when there is a controlling @@ -474,12 +473,6 @@ ModuleMap::ModuleHeaderRole Role, bool isCompilingModuleHeader); - /// Increment the count for the number of times the specified - /// FileEntry has been entered. - void IncrementIncludeCount(const FileEntry *File) { - ++getFileInfo(File).NumIncludes; - } - /// Mark the specified file as having a controlling macro. /// /// This is used by the multiple-include optimization to eliminate diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -450,6 +450,8 @@ ElseLoc(ElseLoc) {} }; + using IncludedFilesSet = llvm::DenseSet; + private: friend class ASTReader; friend class MacroArgs; @@ -718,6 +720,7 @@ using MacroMap = llvm::DenseMap; struct SubmoduleState; + struct SubmoduleIncludeState; /// Information about a submodule that we're currently building. struct BuildingSubmoduleInfo { @@ -736,12 +739,17 @@ /// The number of pending module macro names when we started building this. unsigned OuterPendingModuleMacroNames; + /// The previous SubmoduleIncludeState. + SubmoduleIncludeState *OuterSubmoduleIncludeState; + BuildingSubmoduleInfo(Module *M, SourceLocation ImportLoc, bool IsPragma, SubmoduleState *OuterSubmoduleState, - unsigned OuterPendingModuleMacroNames) + unsigned OuterPendingModuleMacroNames, + SubmoduleIncludeState *OuterSubmoduleIncludeState) : M(M), ImportLoc(ImportLoc), IsPragma(IsPragma), OuterSubmoduleState(OuterSubmoduleState), - OuterPendingModuleMacroNames(OuterPendingModuleMacroNames) {} + OuterPendingModuleMacroNames(OuterPendingModuleMacroNames), + OuterSubmoduleIncludeState(OuterSubmoduleIncludeState) {} }; SmallVector BuildingSubmoduleStack; @@ -765,6 +773,26 @@ /// in a submodule. SubmoduleState *CurSubmoduleState; + /// Information about a (sub)module's preprocessor include state. + struct SubmoduleIncludeState { + /// The files that have been included. + IncludedFilesSet IncludedFiles; + + /// The set of modules that are visible within the submodule. + VisibleModuleSet VisibleModules; + }; + /// The include state for each (sub)module. + std::map SubmoduleIncludeStates; + + /// The include state outside of any (sub)module. + SubmoduleIncludeState NullSubmoduleIncludeState; + + /// The current include state. + SubmoduleIncludeState *CurSubmoduleIncludeState; + + /// The files that have been included. + IncludedFilesSet IncludedFiles; + /// The set of known macros exported from modules. llvm::FoldingSet ModuleMacros; @@ -920,6 +948,9 @@ void updateOutOfDateIdentifier(IdentifierInfo &II) const; + /// Get the external include information for the given (sub)module. + const IncludedFilesSet *getExternalSubmoduleIncludes(Module *M) const; + public: Preprocessor(std::shared_ptr PPOpts, DiagnosticsEngine &diags, LangOptions &opts, SourceManager &SM, @@ -1224,6 +1255,36 @@ /// \} + /// Mark the file as included. + /// Returns true if this is the first time the file was included. + bool markIncluded(const FileEntry *File) { + CurSubmoduleIncludeState->IncludedFiles.insert(File); + return markTransitivelyIncluded(File); + } + + /// Mark the file as transitively included. + /// Returns true if this is the first time the file was included. + bool markTransitivelyIncluded(const FileEntry *File) { + return IncludedFiles.insert(File).second; + } + + /// Return true if this header has already been included. + bool alreadyIncluded(const FileEntry *File) const { + return IncludedFiles.count(File); + } + + /// Get the set of files included outside of any (sub)module. + const IncludedFilesSet &getNullSubmoduleIncludes() const { + return NullSubmoduleIncludeState.IncludedFiles; + } + + /// Get the set of files included in the given (sub)module. + const IncludedFilesSet *getLocalSubmoduleIncludes(Module *M) const { + auto It = SubmoduleIncludeStates.find(M); + return It != SubmoduleIncludeStates.end() ? &It->second.IncludedFiles + : nullptr; + } + /// Return the name of the macro defined before \p Loc that has /// spelling \p Tokens. If there are multiple macros with same spelling, /// return the last one defined. diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -695,6 +695,9 @@ /// Record code for \#pragma float_control options. FLOAT_CONTROL_PRAGMA_OPTIONS = 65, + + /// Record code for included files. + PP_INCLUDED_FILES = 66, }; /// Record types used within a source manager block. @@ -825,6 +828,9 @@ /// Specifies the name of the module that will eventually /// re-export the entities in this module. SUBMODULE_EXPORT_AS = 17, + + /// Specifies files included in this module. + SUBMODULE_INCLUDED_FILES = 18, }; /// Record types used within a comments block. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -927,6 +927,10 @@ /// A list of modules that were imported by precompiled headers or /// any other non-module AST file. SmallVector ImportedModules; + + /// Mapping between a (sub)module and deserialized set of included files. + llvm::DenseMap + SubmoduleIncludedFiles; //@} /// The system include root to be used when loading the @@ -1331,6 +1335,8 @@ llvm::Error ReadSourceManagerBlock(ModuleFile &F); llvm::BitstreamCursor &SLocCursorForID(int ID); SourceLocation getImportLocation(ModuleFile *F); + Preprocessor::IncludedFilesSet readIncludedFiles(ModuleFile &F, + StringRef Blob); ASTReadResult ReadModuleMapFileBlock(RecordData &Record, ModuleFile &F, const ModuleFile *ImportedBy, unsigned ClientLoadCapabilities); @@ -2099,6 +2105,9 @@ /// Module *getSubmodule(serialization::SubmoduleID GlobalID); + /// Return the set of files directly included in the given (sub)module. + const Preprocessor::IncludedFilesSet *getIncludedFiles(Module *M) override; + /// Retrieve the module that corresponds to the given module ID. /// /// Note: overrides method in ExternalASTSource diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -19,6 +19,7 @@ #include "clang/AST/Type.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Sema/Sema.h" #include "clang/Sema/SemaConsumer.h" #include "clang/Serialization/ASTBitCodes.h" @@ -481,6 +482,8 @@ std::set &AffectingModuleMaps); void WriteSourceManagerBlock(SourceManager &SourceMgr, const Preprocessor &PP); + void writeIncludedFiles(raw_ostream &Out, + const Preprocessor::IncludedFilesSet &Files); void WritePreprocessor(const Preprocessor &PP, bool IsModule); void WriteHeaderSearch(const HeaderSearch &HS); void WritePreprocessorDetail(PreprocessingRecord &PPRec, diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h --- a/clang/include/clang/Serialization/ModuleFile.h +++ b/clang/include/clang/Serialization/ModuleFile.h @@ -111,7 +111,8 @@ class ModuleFile { public: ModuleFile(ModuleKind Kind, unsigned Generation) - : Kind(Kind), Generation(Generation) {} + : Kind(Kind), Generation(Generation), + SubmoduleIncludedFiles(SubmoduleIncludedFilesAlloc) {} ~ModuleFile(); // === General information === @@ -394,6 +395,12 @@ /// Remapping table for submodule IDs in this module. ContinuousRangeMap SubmoduleRemap; + /// Allocator for the serialized set of included files. + llvm::BumpPtrAllocator SubmoduleIncludedFilesAlloc; + /// Mapping between (sub)module names and the serialized set of included + /// files. Initialized by the allocator above. + llvm::StringMap SubmoduleIncludedFiles; + // === Selectors === /// The number of selectors new to this file. diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -650,7 +650,7 @@ return; ImportLocs[ID] = Loc; - Vis(M); + Vis(V.M); // Make any exported modules visible. SmallVector Exports; diff --git a/clang/lib/Lex/HeaderSearch.cpp b/clang/lib/Lex/HeaderSearch.cpp --- a/clang/lib/Lex/HeaderSearch.cpp +++ b/clang/lib/Lex/HeaderSearch.cpp @@ -89,16 +89,10 @@ void HeaderSearch::PrintStats() { llvm::errs() << "\n*** HeaderSearch Stats:\n" << FileInfo.size() << " files tracked.\n"; - unsigned NumOnceOnlyFiles = 0, MaxNumIncludes = 0, NumSingleIncludedFiles = 0; - for (unsigned i = 0, e = FileInfo.size(); i != e; ++i) { + unsigned NumOnceOnlyFiles = 0; + for (unsigned i = 0, e = FileInfo.size(); i != e; ++i) NumOnceOnlyFiles += (FileInfo[i].isPragmaOnce || FileInfo[i].isImport); - if (MaxNumIncludes < FileInfo[i].NumIncludes) - MaxNumIncludes = FileInfo[i].NumIncludes; - NumSingleIncludedFiles += FileInfo[i].NumIncludes == 1; - } - llvm::errs() << " " << NumOnceOnlyFiles << " #import/#pragma once files.\n" - << " " << NumSingleIncludedFiles << " included exactly once.\n" - << " " << MaxNumIncludes << " max times a file is included.\n"; + llvm::errs() << " " << NumOnceOnlyFiles << " #import/#pragma once files.\n"; llvm::errs() << " " << NumIncluded << " #include/#include_next/#import.\n" << " " << NumMultiIncludeFileOptzn @@ -1203,7 +1197,6 @@ HFI.isImport |= OtherHFI.isImport; HFI.isPragmaOnce |= OtherHFI.isPragmaOnce; HFI.isModuleHeader |= OtherHFI.isModuleHeader; - HFI.NumIncludes += OtherHFI.NumIncludes; if (!HFI.ControllingMacro && !HFI.ControllingMacroID) { HFI.ControllingMacro = OtherHFI.ControllingMacro; @@ -1364,7 +1357,7 @@ FileInfo.isImport = true; // Has this already been #import'ed or #include'd? - if (FileInfo.NumIncludes && !TryEnterImported()) + if (PP.alreadyIncluded(File) && !TryEnterImported()) return false; } else { // Otherwise, if this is a #include of a file that was previously #import'd @@ -1387,10 +1380,7 @@ } } - // Increment the number of times this file has been included. - ++FileInfo.NumIncludes; - - IsFirstIncludeOfFile = FileInfo.NumIncludes == 1; + IsFirstIncludeOfFile = PP.markIncluded(File); return true; } diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -2055,7 +2055,7 @@ // include cycle. Don't enter already processed files again as it can lead to // reaching the max allowed include depth again. if (Action == Enter && HasReachedMaxIncludeDepth && File && - HeaderInfo.getFileInfo(&File->getFileEntry()).NumIncludes) + alreadyIncluded(*File)) Action = IncludeLimitReached; // Determine whether we should try to import the module for this #include, if diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp --- a/clang/lib/Lex/PPLexerChange.cpp +++ b/clang/lib/Lex/PPLexerChange.cpp @@ -690,9 +690,12 @@ bool ForPragma) { if (!getLangOpts().ModulesLocalVisibility) { // Just track that we entered this submodule. - BuildingSubmoduleStack.push_back( - BuildingSubmoduleInfo(M, ImportLoc, ForPragma, CurSubmoduleState, - PendingModuleMacroNames.size())); + BuildingSubmoduleStack.push_back(BuildingSubmoduleInfo( + M, ImportLoc, ForPragma, CurSubmoduleState, + PendingModuleMacroNames.size(), CurSubmoduleIncludeState)); + + CurSubmoduleIncludeState = &SubmoduleIncludeStates[M]; + if (Callbacks) Callbacks->EnteredSubmodule(M, ImportLoc, ForPragma); return; @@ -735,15 +738,16 @@ } // Track that we entered this module. - BuildingSubmoduleStack.push_back( - BuildingSubmoduleInfo(M, ImportLoc, ForPragma, CurSubmoduleState, - PendingModuleMacroNames.size())); + BuildingSubmoduleStack.push_back(BuildingSubmoduleInfo( + M, ImportLoc, ForPragma, CurSubmoduleState, + PendingModuleMacroNames.size(), CurSubmoduleIncludeState)); if (Callbacks) Callbacks->EnteredSubmodule(M, ImportLoc, ForPragma); // Switch to this submodule as the current submodule. CurSubmoduleState = &State; + CurSubmoduleIncludeState = &SubmoduleIncludeStates[M]; // This module is visible to itself. if (FirstTime) @@ -775,6 +779,8 @@ Module *LeavingMod = Info.M; SourceLocation ImportLoc = Info.ImportLoc; + CurSubmoduleIncludeState = Info.OuterSubmoduleIncludeState; + if (!needModuleMacros() || (!getLangOpts().ModulesLocalVisibility && LeavingMod->getTopLevelModuleName() != getLangOpts().CurrentModule)) { diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -91,7 +91,8 @@ // deferred to Preprocessor::Initialize(). Identifiers(IILookup), PragmaHandlers(new PragmaNamespace(StringRef())), TUKind(TUKind), SkipMainFilePreamble(0, true), - CurSubmoduleState(&NullSubmoduleState) { + CurSubmoduleState(&NullSubmoduleState), + CurSubmoduleIncludeState(&NullSubmoduleIncludeState) { OwnsHeaderSearch = OwnsHeaders; // Default to discarding comments. @@ -548,10 +549,15 @@ // Tell the header info that the main file was entered. If the file is later // #imported, it won't be re-entered. - if (const FileEntry *FE = SourceMgr.getFileEntryForID(MainFileID)) - HeaderInfo.IncrementIncludeCount(FE); + if (const FileEntry *FE = SourceMgr.getFileEntryForID(MainFileID)) { + HeaderInfo.getFileInfo(FE); + markIncluded(FE); + } } + if (Module *M = getCurrentModule()) + CurSubmoduleIncludeState = &SubmoduleIncludeStates[M]; + // Preprocess Predefines to populate the initial preprocessor state. std::unique_ptr SB = llvm::MemoryBuffer::getMemBufferCopy(Predefines, ""); @@ -1315,6 +1321,18 @@ << Message; }); + CurSubmoduleIncludeState->VisibleModules.setVisible( + M, Loc, + [&](Module *M) { + const auto *Includes = getLocalSubmoduleIncludes(M); + if (!Includes) + Includes = getExternalSubmoduleIncludes(M); + if (Includes) + for (const auto &E : *Includes) + markTransitivelyIncluded(E); + }, + [](ArrayRef, Module *, StringRef) {}); + // Add this module to the imports list of the currently-built submodule. if (!BuildingSubmoduleStack.empty() && M != BuildingSubmoduleStack.back().M) BuildingSubmoduleStack.back().M->Imports.insert(M); @@ -1466,3 +1484,8 @@ Record = new PreprocessingRecord(getSourceManager()); addPPCallbacks(std::unique_ptr(Record)); } + +const Preprocessor::IncludedFilesSet * +Preprocessor::getExternalSubmoduleIncludes(Module *M) const { + return ExternalSource ? ExternalSource->getIncludedFiles(M) : nullptr; +} \ No newline at end of file diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -1888,10 +1888,6 @@ HFI.isPragmaOnce |= (Flags >> 4) & 0x01; HFI.DirInfo = (Flags >> 1) & 0x07; HFI.IndexHeaderMapHeader = Flags & 0x01; - // FIXME: Find a better way to handle this. Maybe just store a - // "has been included" flag? - HFI.NumIncludes = std::max(endian::readNext(d), - HFI.NumIncludes); HFI.ControllingMacroID = Reader.getGlobalIdentifierID( M, endian::readNext(d)); if (unsigned FrameworkOffset = @@ -2963,6 +2959,26 @@ } } +Preprocessor::IncludedFilesSet ASTReader::readIncludedFiles(ModuleFile &F, + StringRef Blob) { + using namespace llvm::support; + + Preprocessor::IncludedFilesSet Result; + + const unsigned char *D = (const unsigned char *)Blob.data(); + unsigned FileCount = endian::readNext(D); + + for (unsigned I = 0; I < FileCount; ++D) + for (unsigned Bit = 0; Bit < 8 && I < FileCount; ++Bit, ++I) + if (*D & (1 << Bit)) { + auto InputFileInfo = readInputFileInfo(F, I); + if (auto File = PP.getFileManager().getFile(InputFileInfo.Filename)) + Result.insert(*File); + } + + return Result; +} + llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { BitstreamCursor &Stream = F.Stream; @@ -3701,6 +3717,13 @@ break; } + case PP_INCLUDED_FILES: + if (F.isModule()) + break; + for (const auto &File : readIncludedFiles(F, Blob)) + PP.markTransitivelyIncluded(File); + break; + case LATE_PARSED_TEMPLATE: LateParsedTemplates.emplace_back( std::piecewise_construct, std::forward_as_tuple(&F), @@ -5711,6 +5734,10 @@ CurrentModule->ExportAsModule = Blob.str(); ModMap.addLinkAsDependency(CurrentModule); break; + + case SUBMODULE_INCLUDED_FILES: + F.SubmoduleIncludedFiles.insert( + {CurrentModule->getFullModuleName(), Blob}); } } } @@ -8596,6 +8623,26 @@ return LocalID + I->second; } +const Preprocessor::IncludedFilesSet *ASTReader::getIncludedFiles(Module *M) { + ModuleFile *F = getModuleManager().lookup(M->getASTFile()); + if (!F) + return nullptr; + + auto ResultIt = + SubmoduleIncludedFiles.insert({M, Preprocessor::IncludedFilesSet{}}); + auto &Result = ResultIt.first->second; + if (!ResultIt.second) + return &Result; + + auto It = F->SubmoduleIncludedFiles.find(M->getFullModuleName()); + if (It == F->SubmoduleIncludedFiles.end()) + return nullptr; + auto &Record = It->second; + + Result = readIncludedFiles(*F, Record); + return &Result; +} + Module *ASTReader::getSubmodule(SubmoduleID GlobalID) { if (GlobalID < NUM_PREDEF_SUBMODULE_IDS) { assert(GlobalID == 0 && "Unhandled global submodule ID"); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1765,7 +1765,7 @@ std::pair EmitKeyDataLength(raw_ostream& Out, key_type_ref key, data_type_ref Data) { unsigned KeyLen = key.Filename.size() + 1 + 8 + 8; - unsigned DataLen = 1 + 2 + 4 + 4; + unsigned DataLen = 1 + 4 + 4; for (auto ModInfo : Data.KnownHeaders) if (Writer.getLocalOrImportedSubmoduleID(ModInfo.getModule())) DataLen += 4; @@ -1797,7 +1797,6 @@ | (Data.HFI.DirInfo << 1) | Data.HFI.IndexHeaderMapHeader; LE.write(Flags); - LE.write(Data.HFI.NumIncludes); if (!Data.HFI.ControllingMacro) LE.write(Data.HFI.ControllingMacroID); @@ -2246,6 +2245,26 @@ return false; } +void ASTWriter::writeIncludedFiles( + raw_ostream &Out, const Preprocessor::IncludedFilesSet &Files) { + using namespace llvm::support; + + std::vector IncludedInputFiles; + for (const auto &File : Files) { + auto InputFileIt = InputFileIDs.find(File); + if (InputFileIt == InputFileIDs.end()) + continue; + auto InputFileID = InputFileIt->second; + if (IncludedInputFiles.size() <= InputFileID) + IncludedInputFiles.resize(InputFileID + 1); + IncludedInputFiles[InputFileIt->second] = true; + } + + endian::Writer LE(Out, little); + LE.write(IncludedInputFiles.size()); + Out << bytes(IncludedInputFiles); +} + /// Writes the block containing the serialized form of the /// preprocessor. void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) { @@ -2454,6 +2473,20 @@ MacroOffsetsBase - ASTBlockStartOffset}; Stream.EmitRecordWithBlob(MacroOffsetAbbrev, Record, bytes(MacroOffsets)); } + + { + auto Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(PP_INCLUDED_FILES)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned IncludedFilesAbbrev = Stream.EmitAbbrev(std::move(Abbrev)); + + SmallString<2048> Buffer; + raw_svector_ostream Out(Buffer); + writeIncludedFiles(Out, PP.getNullSubmoduleIncludes()); + RecordData::value_type Record[] = {PP_INCLUDED_FILES}; + Stream.EmitRecordWithBlob(IncludedFilesAbbrev, Record, Buffer.data(), + Buffer.size()); + } } void ASTWriter::WritePreprocessorDetail(PreprocessingRecord &PPRec, @@ -2709,6 +2742,11 @@ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Macro name unsigned ExportAsAbbrev = Stream.EmitAbbrev(std::move(Abbrev)); + Abbrev = std::make_shared(); + Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_INCLUDED_FILES)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned IncludedFilesAbbrev = Stream.EmitAbbrev(std::move(Abbrev)); + // Write the submodule metadata block. RecordData::value_type Record[] = { getNumberOfModules(WritingModule), @@ -2850,6 +2888,15 @@ Stream.EmitRecordWithBlob(ExportAsAbbrev, Record, Mod->ExportAsModule); } + if (const auto *Includes = PP->getLocalSubmoduleIncludes(Mod)) { + SmallString<2048> Buffer; + raw_svector_ostream Out(Buffer); + writeIncludedFiles(Out, *Includes); + RecordData::value_type Record[] = {SUBMODULE_INCLUDED_FILES}; + Stream.EmitRecordWithBlob(IncludedFilesAbbrev, Record, Buffer.data(), + Buffer.size()); + } + // Queue up the submodules of this module. for (auto *M : Mod->submodules()) Q.push(M); diff --git a/clang/test/Modules/import-submodule-visibility.c b/clang/test/Modules/import-submodule-visibility.c new file mode 100644 --- /dev/null +++ b/clang/test/Modules/import-submodule-visibility.c @@ -0,0 +1,99 @@ +// This test checks that imports of headers that appeared in a different submodule than +// what is imported by the current TU don't affect the compilation. + +// RUN: rm -rf %t +// RUN: split-file %s %t + +//--- A.framework/Headers/A.h +#include "Textual.h" +//--- A.framework/Modules/module.modulemap +framework module A { header "A.h" } + +//--- B.framework/Headers/B1.h +#include "Textual.h" +//--- B.framework/Headers/B2.h +//--- B.framework/Modules/module.modulemap +framework module B { + module B1 { header "B1.h" } + module B2 { header "B2.h" } +} + +//--- C/C.h +#include "Textual.h" +//--- C/module.modulemap +module C { header "C.h" } + +//--- D/D1.h +#include "Textual.h" +//--- D/D2.h +//--- D/module.modulemap +module D { + module D1 { header "D1.h" } + module D2 { header "D2.h" } +} + +//--- E/E1.h +#include "E2.h" +//--- E/E2.h +#include "Textual.h" +//--- E/module.modulemap +module E { + module E1 { header "E1.h" } + module E2 { header "E2.h" } +} + +//--- Textual.h +#define MACRO_TEXTUAL 1 + +//--- test.c + +#ifdef A +// +#endif + +#ifdef B +#import +#endif + +#ifdef C +// +#endif + +#ifdef D +#import "D/D2.h" +#endif + +#ifdef E +#import "E/E1.h" +#endif + +#import "Textual.h" + +static int x = MACRO_TEXTUAL; + +// Specifying the PCM file on the command line (without actually importing "A") should not +// prevent "Textual.h" to be included in the TU. +// +// RUN: %clang_cc1 -fmodules -I %t -emit-module %t/A.framework/Modules/module.modulemap -fmodule-name=A -o %t/A.pcm +// RUN: %clang_cc1 -fmodules -I %t -fsyntax-only %t/test.c -DA -fmodule-file=%t/A.pcm + +// Specifying the PCM file on the command line and importing "B2" in the source does not +// prevent "Textual.h" to be included in the TU. +// +// RUN: %clang_cc1 -fmodules -I %t -emit-module %t/B.framework/Modules/module.modulemap -fmodule-name=B -o %t/B.pcm +// RUN: %clang_cc1 -fmodules -I %t -fsyntax-only %t/test.c -DB -iframework %t -fmodule-file=%t/B.pcm + +// Module-only version of the test with framework A. +// +// RUN: %clang_cc1 -fmodules -I %t -emit-module %t/C/module.modulemap -fmodule-name=C -o %t/C.pcm +// RUN: %clang_cc1 -fmodules -I %t -fsyntax-only %t/test.c -DC -fmodule-file=%t/C.pcm + +// Module-only version of the test with framework B. +// +// RUN: %clang_cc1 -fmodules -I %t -emit-module %t/D/module.modulemap -fmodule-name=D -o %t/D.pcm +// RUN: %clang_cc1 -fmodules -I %t -fsyntax-only %t/test.c -DD -fmodule-file=%t/D.pcm + +// Transitively imported, but not exported. +// +// RUN: %clang_cc1 -fmodules -I %t -emit-module %t/E/module.modulemap -fmodule-name=E -o %t/E.pcm +// RUN: %clang_cc1 -fmodules -I %t -fsyntax-only %t/test.c -DE -fmodule-file=%t/E.pcm