From e209e5026892cb07294f733c72bd51359c0f0e72 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 6 Dec 2011 01:10:29 +0000 Subject: [PATCH] Implement inferred submodules support, which (when requested) implicitly generates submodules corresponding to the headers that fall within a module. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@145887 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Lex/ModuleMap.h | 7 + include/clang/Serialization/ASTWriter.h | 6 + lib/Lex/ModuleMap.cpp | 122 +++++++++++++----- lib/Serialization/ASTReader.cpp | 31 +++-- lib/Serialization/ASTWriter.cpp | 39 ++++-- .../Headers/Buried/Treasure.h | 1 + .../Inputs/Module.framework/Headers/Module.h | 3 + .../Inputs/Module.framework/Headers/Sub.h | 2 + test/Modules/inferred-submodules.c | 15 +++ 9 files changed, 167 insertions(+), 59 deletions(-) create mode 100644 test/Modules/Inputs/Module.framework/Headers/Buried/Treasure.h create mode 100644 test/Modules/Inputs/Module.framework/Headers/Sub.h create mode 100644 test/Modules/inferred-submodules.c diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index fdd5ddbf5f..1ef4f674e2 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -177,6 +177,13 @@ class ModuleMap { /// module owns this source location. Module *inferModuleFromLocation(FullSourceLoc Loc); + /// \brief Sets the umbrella header of the given module to the given + /// header. + void setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader); + + /// \brief Adds this header to the given module. + void addHeader(Module *Mod, const FileEntry *Header); + /// \brief Parse the given module map file, and record any modules we /// encounter. /// diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 7dd018d9fe..ab0d1ef3d9 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -101,6 +101,9 @@ class ASTWriter : public ASTDeserializationListener, /// \brief The reader of existing AST files, if we're chaining. ASTReader *Chain; + /// \brief The module we're currently writing, if any. + Module *WritingModule; + /// \brief Indicates when the AST writing is actively performing /// serialization, rather than just queueing updates. bool WritingAST; @@ -368,6 +371,9 @@ class ASTWriter : public ASTDeserializationListener, /// be a positive integer. llvm::DenseMap SubmoduleIDs; + /// \brief Retrieve or create a submodule ID for this module. + unsigned getSubmoduleID(Module *Mod); + /// \brief Write the given subexpression to the bitstream. void WriteSubStmt(Stmt *S, llvm::DenseMap &SubStmtEntries, diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 164eb27e72..498061fc05 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -93,39 +93,75 @@ Module *ModuleMap::findModuleForHeader(const FileEntry *File) { return Known->second; const DirectoryEntry *Dir = File->getDir(); - llvm::DenseMap::iterator KnownDir - = UmbrellaDirs.find(Dir); - if (KnownDir != UmbrellaDirs.end()) - return KnownDir->second; - - // Walk up the directory hierarchy looking for umbrella headers. llvm::SmallVector SkippedDirs; StringRef DirName = Dir->getName(); - do { - // Retrieve our parent path. - DirName = llvm::sys::path::parent_path(DirName); - if (DirName.empty()) - break; - - // Resolve the parent path to a directory entry. - Dir = SourceMgr->getFileManager().getDirectory(DirName); - if (!Dir) - break; - - KnownDir = UmbrellaDirs.find(Dir); + + // Keep walking up the directory hierarchy, looking for a directory with + // an umbrella header. + do { + llvm::DenseMap::iterator KnownDir + = UmbrellaDirs.find(Dir); if (KnownDir != UmbrellaDirs.end()) { Module *Result = KnownDir->second; + Module *TopModule = Result->getTopLevelModule(); + if (TopModule->InferSubmodules) { + // Infer submodules for each of the directories we found between + // the directory of the umbrella header and the directory where + // the actual header is located. + + // For a framework module, the umbrella directory is the framework + // directory, so strip off the "Headers" or "PrivateHeaders". + // FIXME: Should we tack on an "explicit" for PrivateHeaders? That + // might be what we want, but it feels like a hack. + unsigned LastSkippedDir = SkippedDirs.size(); + if (LastSkippedDir && TopModule->IsFramework) + --LastSkippedDir; + + for (unsigned I = LastSkippedDir; I != 0; --I) { + // Find or create the module that corresponds to this directory name. + StringRef Name = llvm::sys::path::stem(SkippedDirs[I-1]->getName()); + Result = findOrCreateModule(Name, Result, /*IsFramework=*/false, + TopModule->InferExplicitSubmodules).first; + + // Associate the module and the directory. + UmbrellaDirs[SkippedDirs[I-1]] = Result; + + // If inferred submodules export everything they import, add a + // wildcard to the set of exports. + if (TopModule->InferExportWildcard && Result->Exports.empty()) + Result->Exports.push_back(Module::ExportDecl(0, true)); + } + + // Infer a submodule with the same name as this header file. + StringRef Name = llvm::sys::path::stem(File->getName()); + Result = findOrCreateModule(Name, Result, /*IsFramework=*/false, + TopModule->InferExplicitSubmodules).first; + + // If inferred submodules export everything they import, add a + // wildcard to the set of exports. + if (TopModule->InferExportWildcard && Result->Exports.empty()) + Result->Exports.push_back(Module::ExportDecl(0, true)); + } else { + // Record each of the directories we stepped through as being part of + // the module we found, since the umbrella header covers them all. + for (unsigned I = 0, N = SkippedDirs.size(); I != N; ++I) + UmbrellaDirs[SkippedDirs[I]] = Result; + } - // Record each of the directories we stepped through as being part of - // the module we found, since the umbrella header covers them all. - for (unsigned I = 0, N = SkippedDirs.size(); I != N; ++I) - UmbrellaDirs[SkippedDirs[I]] = Result; - + Headers[File] = Result; return Result; } SkippedDirs.push_back(Dir); - } while (true); + + // Retrieve our parent path. + DirName = llvm::sys::path::parent_path(DirName); + if (DirName.empty()) + break; + + // Resolve the parent path to a directory entry. + Dir = SourceMgr->getFileManager().getDirectory(DirName); + } while (Dir); return 0; } @@ -205,10 +241,31 @@ ModuleMap::inferFrameworkModule(StringRef ModuleName, // export * Result->Exports.push_back(Module::ExportDecl(0, true)); + // module * { export * } + Result->InferSubmodules = true; + Result->InferExportWildcard = true; + Modules[ModuleName] = Result; return Result; } +void ModuleMap::setUmbrellaHeader(Module *Mod, const FileEntry *UmbrellaHeader){ + Headers[UmbrellaHeader] = Mod; + Mod->UmbrellaHeader = UmbrellaHeader; + + const DirectoryEntry *UmbrellaDir = UmbrellaHeader->getDir(); + if (Mod->IsFramework) + UmbrellaDir = SourceMgr->getFileManager().getDirectory( + llvm::sys::path::parent_path(UmbrellaDir->getName())); + + UmbrellaDirs[UmbrellaDir] = Mod; +} + +void ModuleMap::addHeader(Module *Mod, const FileEntry *Header) { + Mod->Headers.push_back(Header); + Headers[Header] = Mod; +} + const FileEntry * ModuleMap::getContainingModuleMapFile(Module *Module) { if (Module->DefinitionLoc.isInvalid() || !SourceMgr) @@ -683,19 +740,25 @@ void ModuleMapParser::parseUmbrellaDecl() { // FIXME: We shouldn't be eagerly stat'ing every file named in a module map. // Come up with a lazy way to do this. if (File) { + const DirectoryEntry *UmbrellaDir = File->getDir(); + if (ActiveModule->IsFramework) { + // For framework modules, use the framework directory as the umbrella + // directory. + UmbrellaDir = SourceMgr.getFileManager().getDirectory( + llvm::sys::path::parent_path(UmbrellaDir->getName())); + } + if (const Module *OwningModule = Map.Headers[File]) { Diags.Report(FileNameLoc, diag::err_mmap_header_conflict) << FileName << OwningModule->getFullModuleName(); HadError = true; - } else if ((OwningModule = Map.UmbrellaDirs[Directory])) { + } else if ((OwningModule = Map.UmbrellaDirs[UmbrellaDir])) { Diags.Report(UmbrellaLoc, diag::err_mmap_umbrella_clash) << OwningModule->getFullModuleName(); HadError = true; } else { // Record this umbrella header. - ActiveModule->UmbrellaHeader = File; - Map.Headers[File] = ActiveModule; - Map.UmbrellaDirs[Directory] = ActiveModule; + Map.setUmbrellaHeader(ActiveModule, File); } } else { Diags.Report(FileNameLoc, diag::err_mmap_header_not_found) @@ -743,8 +806,7 @@ void ModuleMapParser::parseHeaderDecl() { HadError = true; } else { // Record this file. - ActiveModule->Headers.push_back(File); - Map.Headers[File] = ActiveModule; + Map.addHeader(ActiveModule, File); } } else { Diags.Report(FileNameLoc, diag::err_mmap_header_not_found) diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index ebc1795a26..636574aa93 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -3049,18 +3049,19 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) { return Failure; } - if (Record.size() < 6) { + if (Record.size() < 7) { Error("malformed module definition"); return Failure; } StringRef Name(BlobStart, BlobLen); - unsigned Parent = getGlobalSubmoduleID(F, Record[0]); - bool IsFramework = Record[1]; - bool IsExplicit = Record[2]; - bool InferSubmodules = Record[3]; - bool InferExplicitSubmodules = Record[4]; - bool InferExportWildcard = Record[5]; + SubmoduleID GlobalID = getGlobalSubmoduleID(F, Record[0]); + SubmoduleID Parent = getGlobalSubmoduleID(F, Record[1]); + bool IsFramework = Record[2]; + bool IsExplicit = Record[3]; + bool InferSubmodules = Record[4]; + bool InferExplicitSubmodules = Record[5]; + bool InferExportWildcard = Record[6]; Module *ParentModule = 0; if (Parent) @@ -3071,9 +3072,9 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) { CurrentModule = ModMap.findOrCreateModule(Name, ParentModule, IsFramework, IsExplicit).first; - - if (CurrentModuleGlobalIndex >= SubmodulesLoaded.size() || - SubmodulesLoaded[CurrentModuleGlobalIndex]) { + SubmoduleID GlobalIndex = GlobalID - NUM_PREDEF_SUBMODULE_IDS; + if (GlobalIndex >= SubmodulesLoaded.size() || + SubmodulesLoaded[GlobalIndex]) { Error("too many submodules"); return Failure; } @@ -3082,11 +3083,9 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) { CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules; CurrentModule->InferExportWildcard = InferExportWildcard; if (DeserializationListener) - DeserializationListener->ModuleRead( - CurrentModuleGlobalIndex + NUM_PREDEF_SUBMODULE_IDS, - CurrentModule); + DeserializationListener->ModuleRead(GlobalID, CurrentModule); - SubmodulesLoaded[CurrentModuleGlobalIndex++] = CurrentModule; + SubmodulesLoaded[GlobalIndex] = CurrentModule; break; } @@ -3102,7 +3101,7 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) { StringRef FileName(BlobStart, BlobLen); if (const FileEntry *Umbrella = PP.getFileManager().getFile(FileName)) { if (!CurrentModule->UmbrellaHeader) - CurrentModule->UmbrellaHeader = Umbrella; + ModMap.setUmbrellaHeader(CurrentModule, Umbrella); else if (CurrentModule->UmbrellaHeader != Umbrella) { Error("mismatched umbrella headers in submodule"); return Failure; @@ -3126,7 +3125,7 @@ ASTReader::ASTReadResult ASTReader::ReadSubmoduleBlock(ModuleFile &F) { if (std::find(CurrentModule->Headers.begin(), CurrentModule->Headers.end(), File) == CurrentModule->Headers.end()) - CurrentModule->Headers.push_back(File); + ModMap.addHeader(CurrentModule, File); } break; } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index d31eef9e19..946bc76c40 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1845,6 +1845,14 @@ void ASTWriter::WritePreprocessorDetail(PreprocessingRecord &PPRec) { } } +unsigned ASTWriter::getSubmoduleID(Module *Mod) { + llvm::DenseMap::iterator Known = SubmoduleIDs.find(Mod); + if (Known != SubmoduleIDs.end()) + return Known->second; + + return SubmoduleIDs[Mod] = NextSubmoduleID++; +} + /// \brief Compute the number of modules within the given tree (including the /// given module). static unsigned getNumberOfModules(Module *Mod) { @@ -1881,6 +1889,7 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { using namespace llvm; BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_DEFINITION)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // ID Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Parent Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsFramework Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExplicit @@ -1912,12 +1921,12 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { while (!Q.empty()) { Module *Mod = Q.front(); Q.pop(); - assert(SubmoduleIDs.find(Mod) == SubmoduleIDs.end()); - SubmoduleIDs[Mod] = NextSubmoduleID++; + unsigned ID = getSubmoduleID(Mod); // Emit the definition of the block. Record.clear(); Record.push_back(SUBMODULE_DEFINITION); + Record.push_back(ID); if (Mod->Parent) { assert(SubmoduleIDs[Mod->Parent] && "Submodule parent not written?"); Record.push_back(SubmoduleIDs[Mod->Parent]); @@ -1988,11 +1997,14 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { } Stream.ExitBlock(); + + assert((NextSubmoduleID - FirstSubmoduleID + == getNumberOfModules(WritingModule)) && "Wrong # of submodules"); } serialization::SubmoduleID ASTWriter::inferSubmoduleIDFromLocation(SourceLocation Loc) { - if (Loc.isInvalid() || SubmoduleIDs.empty()) + if (Loc.isInvalid() || !WritingModule) return 0; // No submodule // Find the module that owns this location. @@ -2002,13 +2014,11 @@ ASTWriter::inferSubmoduleIDFromLocation(SourceLocation Loc) { if (!OwningMod) return 0; - // Check whether we known about this submodule. - llvm::DenseMap::iterator Known - = SubmoduleIDs.find(OwningMod); - if (Known == SubmoduleIDs.end()) + // Check whether this submodule is part of our own module. + if (WritingModule != OwningMod && !OwningMod->isSubModuleOf(WritingModule)) return 0; - return Known->second; + return getSubmoduleID(OwningMod); } void ASTWriter::WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag) { @@ -2933,7 +2943,8 @@ void ASTWriter::SetSelectorOffset(Selector Sel, uint32_t Offset) { } ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream) - : Stream(Stream), Context(0), PP(0), Chain(0), WritingAST(false), + : Stream(Stream), Context(0), PP(0), Chain(0), WritingModule(0), + WritingAST(false), FirstDeclID(NUM_PREDEF_DECL_IDS), NextDeclID(FirstDeclID), FirstTypeID(NUM_PREDEF_TYPE_IDS), NextTypeID(FirstTypeID), FirstIdentID(NUM_PREDEF_IDENT_IDS), NextIdentID(FirstIdentID), @@ -2975,9 +2986,11 @@ void ASTWriter::WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls, Context = &SemaRef.Context; PP = &SemaRef.PP; + this->WritingModule = WritingModule; WriteASTCore(SemaRef, StatCalls, isysroot, OutputFile, WritingModule); Context = 0; PP = 0; + this->WritingModule = 0; WritingAST = false; } @@ -3201,10 +3214,6 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, AddTypeRef(Context.ObjCSelRedefinitionType, SpecialTypes); AddTypeRef(Context.getucontext_tType(), SpecialTypes); - // If we're emitting a module, write out the submodule information. - if (WritingModule) - WriteSubmodules(WritingModule); - // Keep writing types and declarations until all types and // declarations have been written. Stream.EnterSubblock(DECLTYPES_BLOCK_ID, NUM_ALLOWED_ABBREVS_SIZE); @@ -3282,6 +3291,10 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, WriteCXXBaseSpecifiersOffsets(); + // If we're emitting a module, write out the submodule information. + if (WritingModule) + WriteSubmodules(WritingModule); + Stream.EmitRecord(SPECIAL_TYPES, SpecialTypes); /// Build a record containing first declarations from a chained PCH and the diff --git a/test/Modules/Inputs/Module.framework/Headers/Buried/Treasure.h b/test/Modules/Inputs/Module.framework/Headers/Buried/Treasure.h new file mode 100644 index 0000000000..6e81adcb2b --- /dev/null +++ b/test/Modules/Inputs/Module.framework/Headers/Buried/Treasure.h @@ -0,0 +1 @@ +unsigned *Buried_Treasure; diff --git a/test/Modules/Inputs/Module.framework/Headers/Module.h b/test/Modules/Inputs/Module.framework/Headers/Module.h index 9bf6a27edf..be88bb5adc 100644 --- a/test/Modules/Inputs/Module.framework/Headers/Module.h +++ b/test/Modules/Inputs/Module.framework/Headers/Module.h @@ -14,4 +14,7 @@ const char *getModuleVersion(void); #define MODULE_H_MACRO 1 #__private_macro__ MODULE_H_MACRO +#include +#include + #endif // MODULE_H diff --git a/test/Modules/Inputs/Module.framework/Headers/Sub.h b/test/Modules/Inputs/Module.framework/Headers/Sub.h new file mode 100644 index 0000000000..64be702fdc --- /dev/null +++ b/test/Modules/Inputs/Module.framework/Headers/Sub.h @@ -0,0 +1,2 @@ +int *Module_Sub; + diff --git a/test/Modules/inferred-submodules.c b/test/Modules/inferred-submodules.c new file mode 100644 index 0000000000..9c164d8c52 --- /dev/null +++ b/test/Modules/inferred-submodules.c @@ -0,0 +1,15 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -x objective-c -Wauto-import -fmodule-cache-path %t -fauto-module-import -F %S/Inputs %s -verify + +__import_module__ Module.Sub; + +void test_Module_Sub() { + int *ip = Module_Sub; +} + +__import_module__ Module.Buried.Treasure; + +void dig() { + unsigned *up = Buried_Treasure; +} +