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 @@ -40,6 +40,9 @@ /// Map a module ID to a module. virtual Module *getModule(unsigned ModuleID) = 0; + + /// Return the included files of the given (sub)module. + virtual const llvm::StringMap *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 @@ -89,9 +89,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 @@ -130,13 +127,6 @@ /// any. const IdentifierInfo * getControllingMacro(ExternalPreprocessorSource *External); - - /// Determine whether this is a non-default header file info, e.g., - /// it corresponds to an actual header we've included or tried to include. - bool isNonDefault() const { - return isImport || isPragmaOnce || NumIncludes || ControllingMacro || - ControllingMacroID; - } }; /// An external source of header file information, which may supply @@ -481,12 +471,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 @@ -496,11 +480,6 @@ getFileInfo(File).ControllingMacro = ControllingMacro; } - /// Return true if this is the first time encountering this header. - bool FirstTimeLexingFile(const FileEntry *File) { - return getFileInfo(File).NumIncludes == 1; - } - /// Determine whether this file is intended to be safe from /// multiple inclusions, e.g., it has \#pragma once or a controlling /// macro. 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 @@ -718,6 +718,7 @@ using MacroMap = llvm::DenseMap; struct SubmoduleState; + struct SubmoduleIncludeState; /// Information about a submodule that we're currently building. struct BuildingSubmoduleInfo { @@ -736,12 +737,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 +771,26 @@ /// in a submodule. SubmoduleState *CurSubmoduleState; + /// Information about a (sub)module's preprocessor include state. + struct SubmoduleIncludeState { + /// For each included file, we track the number of includes. + llvm::DenseMap 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; + + /// For each included file, we track the number of includes. + llvm::DenseMap IncludedFiles; + /// The set of known macros exported from modules. llvm::FoldingSet ModuleMacros; @@ -920,6 +946,10 @@ void updateOutOfDateIdentifier(IdentifierInfo &II) const; + /// Get the external include information for the given (sub)module. + const llvm::StringMap * + getExternalSubmoduleIncludes(Module *M) const; + public: Preprocessor(std::shared_ptr PPOpts, DiagnosticsEngine &diags, LangOptions &opts, SourceManager &SM, @@ -1224,6 +1254,26 @@ /// \} + /// Check whether the file has been included at least once. + bool includedAtLeastOnce(const FileEntry *File) const; + + /// Check whether this is the first time encountering the header file. + bool firstTimeLexingFile(const FileEntry *File) const; + + /// Bump the include counter for the given file. + void incrementIncludeCount(StringRef Filename, unsigned Count = 1); + void incrementIncludeCount(const FileEntry *File, unsigned Count = 1); + + /// Get the include information outside of any (sub)module. + const llvm::DenseMap & + getNullSubmoduleIncludes() const { + return NullSubmoduleIncludeState.IncludedFiles; + } + + /// Get the include information for the given local (sub)module. + const llvm::DenseMap * + getLocalSubmoduleIncludes(Module *M) const; + /// 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 includes outside of any (sub)module, used by PCH. + PP_NUM_INCLUDES = 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 the number of includes of each file. + SUBMODULE_NUM_INCLUDES = 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,9 @@ /// A list of modules that were imported by precompiled headers or /// any other non-module AST file. SmallVector ImportedModules; + + /// Mapping between a read (sub)module and the file include information. + llvm::DenseMap> SubmoduleIncludedFiles; //@} /// The system include root to be used when loading the @@ -1380,6 +1383,9 @@ RecordLocation getLocalBitOffset(uint64_t GlobalOffset); uint64_t getGlobalBitOffset(ModuleFile &M, uint64_t LocalOffset); + /// Read the record containing files included in a (sub)module. + void readIncludedFiles(llvm::StringMap &Result, RecordData &Record); + /// Returns the first preprocessed entity ID that begins or ends after /// \arg Loc. serialization::PreprocessedEntityID @@ -2099,6 +2105,9 @@ /// Module *getSubmodule(serialization::SubmoduleID GlobalID); + /// Return the included files of the given (sub)module. + const llvm::StringMap *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 @@ -592,6 +592,11 @@ void AddAlignPackInfo(const Sema::AlignPackInfo &Info, RecordDataImpl &Record); + /// Emit included files information. + void + addIncludedFiles(const llvm::DenseMap &Files, + RecordDataImpl &Record); + /// Emit a source location. void AddSourceLocation(SourceLocation Loc, RecordDataImpl &Record); 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 @@ -394,6 +394,10 @@ /// Remapping table for submodule IDs in this module. ContinuousRangeMap SubmoduleRemap; + /// Mapping between (sub)module names and the serialized version of file + /// include information. + llvm::StringMap> SubmoduleIncludedFiles; + // === Selectors === /// The number of selectors new to this file. 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; @@ -1361,7 +1354,7 @@ FileInfo.isImport = true; // Has this already been #import'ed or #include'd? - if (FileInfo.NumIncludes && !TryEnterImported()) + if (PP.includedAtLeastOnce(File) && !TryEnterImported()) return false; } else { // Otherwise, if this is a #include of a file that was previously #import'd @@ -1384,8 +1377,7 @@ } } - // Increment the number of times this file has been included. - ++FileInfo.NumIncludes; + PP.incrementIncludeCount(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 @@ -2051,7 +2051,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) + includedAtLeastOnce(*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 @@ -376,8 +376,7 @@ if (const IdentifierInfo *DefinedMacro = CurPPLexer->MIOpt.GetDefinedMacro()) { if (!isMacroDefined(ControllingMacro) && - DefinedMacro != ControllingMacro && - HeaderInfo.FirstTimeLexingFile(FE)) { + DefinedMacro != ControllingMacro && firstTimeLexingFile(FE)) { // If the edit distance between the two macros is more than 50%, // DefinedMacro may not be header guard, or can be header guard of @@ -688,9 +687,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; @@ -733,15 +735,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) @@ -773,6 +776,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. @@ -286,6 +287,15 @@ << " token paste (##) operations performed, " << NumFastTokenPaste << " on the fast path.\n"; + unsigned NumSingleIncludedFiles = 0, MaxNumIncludes = 0; + for (const auto &Include : IncludedFiles) { + if (MaxNumIncludes < Include.second) + MaxNumIncludes = Include.second; + NumSingleIncludedFiles += Include.second == 1; + } + llvm::errs() << NumSingleIncludedFiles << " files included exactly once.\n" + << MaxNumIncludes << " max times a file is included.\n"; + llvm::errs() << "\nPreprocessor Memory: " << getTotalMemory() << "B total"; llvm::errs() << "\n BumpPtr: " << BP.getTotalMemory(); @@ -548,10 +558,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)) { + (void)getHeaderSearchInfo().getFileInfo(FE); + incrementIncludeCount(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 +1330,21 @@ << Message; }); + CurSubmoduleIncludeState->VisibleModules.setVisible( + M, Loc, + [&](Module *M) { + if (const auto *Includes = getLocalSubmoduleIncludes(M)) { + for (const auto &E : *Includes) + incrementIncludeCount(E.getFirst(), E.getSecond()); + return; + } + + if (const auto *Includes = getExternalSubmoduleIncludes(M)) + for (const auto &E : *Includes) + incrementIncludeCount(E.getKey(), E.getValue()); + }, + [](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 +1496,42 @@ Record = new PreprocessingRecord(getSourceManager()); addPPCallbacks(std::unique_ptr(Record)); } + +bool Preprocessor::includedAtLeastOnce(const FileEntry *File) const { + auto It = IncludedFiles.find(File); + if (It == IncludedFiles.end()) + return false; + return It->second >= 1; +} + +bool Preprocessor::firstTimeLexingFile(const FileEntry *File) const { + auto It = IncludedFiles.find(File); + if (It == IncludedFiles.end()) + return false; + return It->second == 1; +} + +void Preprocessor::incrementIncludeCount(StringRef Filename, unsigned N) { + if (auto File = FileMgr.getFile(Filename)) + incrementIncludeCount(*File, N); +} + +void Preprocessor::incrementIncludeCount(const FileEntry *File, unsigned N) { + CurSubmoduleIncludeState->IncludedFiles[File] += N; + IncludedFiles[File] += N; +} + +const llvm::DenseMap * +Preprocessor::getLocalSubmoduleIncludes(Module *M) const { + auto It = SubmoduleIncludeStates.find(M); + if (It != SubmoduleIncludeStates.end()) + return &It->second.IncludedFiles; + return nullptr; +} + +const llvm::StringMap * +Preprocessor::getExternalSubmoduleIncludes(Module *M) const { + if (ExternalSource) + return ExternalSource->getIncludedFiles(M); + return nullptr; +} 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 = @@ -3701,6 +3697,17 @@ break; } + case PP_NUM_INCLUDES: { + if (F.isModule()) + break; + + llvm::StringMap Includes; + readIncludedFiles(Includes, Record); + for (const auto &E : Includes) + PP.incrementIncludeCount(E.getKey(), E.getValue()); + break; + } + case LATE_PARSED_TEMPLATE: LateParsedTemplates.emplace_back( std::piecewise_construct, std::forward_as_tuple(&F), @@ -5711,6 +5718,10 @@ CurrentModule->ExportAsModule = Blob.str(); ModMap.addLinkAsDependency(CurrentModule); break; + + case SUBMODULE_NUM_INCLUDES: + F.SubmoduleIncludedFiles.insert( + {CurrentModule->getFullModuleName(), Record}); } } } @@ -8594,6 +8605,38 @@ return LocalID + I->second; } +void ASTReader::readIncludedFiles(llvm::StringMap &Result, + RecordData &Record) { + unsigned Idx = 0; + unsigned TotalCount = Record[Idx++]; + unsigned IncludedOnceCount = Record[Idx++]; + + for (unsigned I = 0; I < IncludedOnceCount; ++I) + Result.insert({ReadString(Record, Idx), 1}); + for (unsigned I = 0; I < TotalCount - IncludedOnceCount; ++I) + Result.insert({ReadString(Record, Idx), 2}); +} + +const llvm::StringMap *ASTReader::getIncludedFiles(Module *M) { + ModuleFile *F = getModuleManager().lookup(M->getASTFile()); + if (!F) + return nullptr; + + auto ResultIt = + SubmoduleIncludedFiles.insert({M, llvm::StringMap{}}); + 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; + + readIncludedFiles(Result, 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 @@ -1696,7 +1696,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; @@ -1728,7 +1728,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); @@ -2171,6 +2170,35 @@ return false; } +void ASTWriter::addIncludedFiles( + const llvm::DenseMap &Files, + RecordDataImpl &Record) { + auto Vec = llvm::to_vector<0>(Files); + + // Ensure the order is stable. + llvm::sort(Vec, [](const auto &LHS, const auto &RHS) { + return LHS.first->getName().compare(RHS.first->getName()) < 0; + }); + + // The first partition contains files included exactly once, + // the second partition contains files included more than once. + auto It = std::stable_partition(Vec.begin(), Vec.end(), + [](const auto &E) { return E.second == 1; }); + + unsigned TotalCount = Vec.size(); + unsigned ExactlyOnceCount = std::distance(Vec.begin(), It); + + Record.push_back(TotalCount); + // Encode the partition point into the record header. This way, entries can + // only contain the file name, omitting the include count information. + Record.push_back(ExactlyOnceCount); + + for (const auto &E : Vec) { + assert(E.second != 0 && "Serializing only included files."); + AddString(E.first->getName(), Record); + } +} + /// Writes the block containing the serialized form of the /// preprocessor. void ASTWriter::WritePreprocessor(const Preprocessor &PP, bool IsModule) { @@ -2379,6 +2407,10 @@ MacroOffsetsBase - ASTBlockStartOffset}; Stream.EmitRecordWithBlob(MacroOffsetAbbrev, Record, bytes(MacroOffsets)); } + + addIncludedFiles(PP.getNullSubmoduleIncludes(), Record); + Stream.EmitRecord(PP_NUM_INCLUDES, Record); + Record.clear(); } void ASTWriter::WritePreprocessorDetail(PreprocessingRecord &PPRec, @@ -2775,6 +2807,13 @@ Stream.EmitRecordWithBlob(ExportAsAbbrev, Record, Mod->ExportAsModule); } + if (const auto *Includes = PP->getLocalSubmoduleIncludes(Mod)) { + RecordData Record; + addIncludedFiles(*Includes, Record); + Stream.EmitRecord(SUBMODULE_NUM_INCLUDES, Record); + Record.clear(); + } + // 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,80 @@ +// 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" } +} + +//--- 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 + +#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 -fmodule-map-file=%t/A.framework/Modules/module.modulemap + +// 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 -fmodule-map-file=%t/B.framework/Modules/module.modulemap + +// 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 -fmodule-map-file=%t/C/module.modulemap + +// 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 -fmodule-map-file=%t/D/module.modulemap