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 @@ -110,6 +110,10 @@ /// of the framework. StringRef Framework; + /// Stores modules including this header. + /// Bool represents if the header was included or imported. + llvm::DenseMap ModuleIncludersMap; + HeaderFileInfo() : isImport(false), isPragmaOnce(false), DirInfo(SrcMgr::C_User), External(false), isModuleHeader(false), isCompilingModuleHeader(false), @@ -457,6 +461,11 @@ ModuleMap::ModuleHeaderRole Role, bool isCompilingModuleHeader); + void MarkFileIncludedFromModule(const FileEntry *File, const Module *M, + bool isImport) { + getFileInfo(File).ModuleIncludersMap[M] |= isImport; + } + /// Increment the count for the number of times the specified /// FileEntry has been entered. void IncrementIncludeCount(const FileEntry *File) { 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 @@ -41,7 +41,7 @@ /// Version 4 of AST files also requires that the version control branch and /// revision match exactly, since there is no backward compatibility of /// AST files at this time. -const unsigned VERSION_MAJOR = 13; +const unsigned VERSION_MAJOR = 14; /// AST file minor version number supported by this version of /// Clang. 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 @@ -2144,6 +2144,15 @@ Action = (SuggestedModule && !getLangOpts().CompilingPCH) ? Import : Skip; } + if (File) { + // Store extra data for inclusion from a modular header. + Module *RequestingModule = getModuleForLocation(HashLoc); + if (RequestingModule) { + HeaderInfo.MarkFileIncludedFromModule(&File->getFileEntry(), + RequestingModule, EnterOnce); + } + } + // Check for circular inclusion of the main file. // We can't generate a consistent preamble with regard to the conditional // stack if the main file is included again as due to the preamble bounds 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 @@ -1887,14 +1887,9 @@ HeaderFileInfo HFI; unsigned Flags = *d++; // FIXME: Refactor with mergeHeaderFileInfo in HeaderSearch.cpp. - HFI.isImport |= (Flags >> 5) & 0x01; 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 = @@ -1905,6 +1900,23 @@ HFI.Framework = HS->getUniqueFrameworkName(FrameworkName); } + while (true) { + uint32_t LocalSMID = endian::readNext(d); + if (!LocalSMID) + break; + bool IsImport = static_cast(LocalSMID & 1); + LocalSMID >>= 1; + + SubmoduleID GlobalSMID = Reader.getGlobalSubmoduleID(M, LocalSMID); + Module *Mod = Reader.getSubmodule(GlobalSMID); + HFI.ModuleIncludersMap[Mod] = IsImport; + // Update isImport and NumIncludes based on including module. + if (Mod->NameVisibility == Module::NameVisibilityKind::AllVisible) { + HFI.isImport |= IsImport; + ++HFI.NumIncludes; + } + } + assert((End - d) % 4 == 0 && "Wrong data length in HeaderFileInfo deserialization"); while (d != End) { @@ -1930,6 +1942,13 @@ HFI.isModuleHeader |= !(HeaderRole & ModuleMap::TextualHeader); } + serialization::ModuleKind PrimaryModuleKind = + Reader.getModuleManager().getPrimaryModule().Kind; + if ((PrimaryModuleKind == MK_PCH) || (PrimaryModuleKind == MK_Preamble)) { + // Treat preprocessor/preamble header as included at least once. + ++HFI.NumIncludes; + } + // This HeaderFileInfo was externally loaded. HFI.External = true; HFI.IsValid = true; 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 @@ -1668,7 +1668,10 @@ 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 + 4; + for (auto ModuleIncluder : Data.HFI.ModuleIncludersMap) + if (Writer.getLocalOrImportedSubmoduleID(ModuleIncluder.first)) + DataLen += 4; for (auto ModInfo : Data.KnownHeaders) if (Writer.getLocalOrImportedSubmoduleID(ModInfo.getModule())) DataLen += 4; @@ -1695,12 +1698,10 @@ endian::Writer LE(Out, little); uint64_t Start = Out.tell(); (void)Start; - unsigned char Flags = (Data.HFI.isImport << 5) - | (Data.HFI.isPragmaOnce << 4) - | (Data.HFI.DirInfo << 1) - | Data.HFI.IndexHeaderMapHeader; + unsigned char Flags = (Data.HFI.isPragmaOnce << 4) | + (Data.HFI.DirInfo << 1) | + Data.HFI.IndexHeaderMapHeader; LE.write(Flags); - LE.write(Data.HFI.NumIncludes); if (!Data.HFI.ControllingMacro) LE.write(Data.HFI.ControllingMacroID); @@ -1723,6 +1724,18 @@ } LE.write(Offset); + for (auto ModuleIncluder : Data.HFI.ModuleIncludersMap) { + if (uint32_t ModID = + Writer.getLocalOrImportedSubmoduleID(ModuleIncluder.first)) { + uint32_t Value = (ModID << 1) | (bool)ModuleIncluder.second; + assert((Value >> 1) == ModID && "overflow in header module info"); + LE.write(Value); + } + } + // Using a stop value because cannot tell ahead of time how many includers + // are local or imported submodules. + LE.write(0); + auto EmitModule = [&](Module *M, ModuleMap::ModuleHeaderRole Role) { if (uint32_t ModID = Writer.getLocalOrImportedSubmoduleID(M)) { uint32_t Value = (ModID << 2) | (unsigned)Role; diff --git a/clang/test/Modules/Inputs/import-nonmodular/Modularized.framework/Headers/Invisible.h b/clang/test/Modules/Inputs/import-nonmodular/Modularized.framework/Headers/Invisible.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/import-nonmodular/Modularized.framework/Headers/Invisible.h @@ -0,0 +1 @@ +#import diff --git a/clang/test/Modules/Inputs/import-nonmodular/Modularized.framework/Headers/Visible.h b/clang/test/Modules/Inputs/import-nonmodular/Modularized.framework/Headers/Visible.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/import-nonmodular/Modularized.framework/Headers/Visible.h @@ -0,0 +1 @@ +// Empty. diff --git a/clang/test/Modules/Inputs/import-nonmodular/Modularized.framework/Modules/module.modulemap b/clang/test/Modules/Inputs/import-nonmodular/Modularized.framework/Modules/module.modulemap new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/import-nonmodular/Modularized.framework/Modules/module.modulemap @@ -0,0 +1,11 @@ +framework module Modularized { + module Visible { + header "Visible.h" + export * + } + + module Invisible { + header "Invisible.h" + export * + } +} diff --git a/clang/test/Modules/Inputs/import-nonmodular/Unmodularized.framework/Headers/Unmodularized.h b/clang/test/Modules/Inputs/import-nonmodular/Unmodularized.framework/Headers/Unmodularized.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/import-nonmodular/Unmodularized.framework/Headers/Unmodularized.h @@ -0,0 +1,2 @@ +typedef struct UnmodularizedStruct { +} UnmodularizedStruct; diff --git a/clang/test/Modules/import-nonmodular.m b/clang/test/Modules/import-nonmodular.m new file mode 100644 --- /dev/null +++ b/clang/test/Modules/import-nonmodular.m @@ -0,0 +1,18 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fsyntax-only -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -F %S/Inputs/import-nonmodular %s -verify +// expected-no-diagnostics + +// Test non-modular headers are not skipped even if they are imported by +// non-imported submodules of an imported module. Dependency graph is +// +// Unmodularized.h +// ^ ^ +// | | +// Modularized.Visible | Modularized.Invisible +// ^ | +// \ | +// import-nonmodular.m + +#import +#import +void test(UnmodularizedStruct *s) {}