Skip to content

Commit cea7c58

Browse files
committed
[clang][modules] Lazily load by name lookups in module maps
Instead of eagerly populating the `clang::ModuleMap` when looking up a module by name, this patch changes `HeaderSearch` to only load the modules that are actually used. This introduces `ModuleMap::findOrLoadModule` which will load modules from parsed but not loaded module maps. This cannot be used anywhere that the module loading code calls into as it can create infinite recursion. This currently just reparses module maps when looking up a module by header. This is fine as redeclarations are allowed from the same file, but future patches will also make looking up a module by header lazy. This patch changes the shadow.m test to use explicitly built modules and `#import`. This test and the shadow feature are very brittle and do not work in general. The test relied on pcm files being left behind by prior failing clang invocations that were then reused by the last invocation. If you clean the cache then the last invocation will always fail. This is because the input module map and the `-fmodule-map-file=` module map are parsed in the same module scope, and `-fmodule-map-file=` is forwarded to implicit module builds. That means you are guaranteed to hit a module redeclaration error if the TU actually imports the module it is trying to shadow. This patch changes when we load A2's module map to after the `A` module has been loaded, which sets the `IsFromModuleFile` bit on `A`. This means that A2's `A` is skipped entirely instead of creating a shadow module, and we get textual inclusion. It is possible to construct a case where this would happen before this patch too. An upcoming patch in this series will rework shadowing to work in the general case, but that's only possible once header -> module lookup is lazy too.
1 parent 27e8e08 commit cea7c58

20 files changed

+457
-136
lines changed

clang-tools-extra/modularize/ModularizeUtilities.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ std::error_code ModularizeUtilities::loadModuleMap(
290290
Target.get(), *HeaderInfo));
291291

292292
// Parse module.modulemap file into module map.
293-
if (ModMap->loadModuleMapFile(ModuleMapEntry, false, Dir)) {
293+
if (ModMap->parseAndLoadModuleMapFile(ModuleMapEntry, false, Dir)) {
294294
return std::error_code(1, std::generic_category());
295295
}
296296

clang/include/clang/Basic/DiagnosticGroups.td

+1
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ def ModuleImport : DiagGroup<"module-import">;
606606
def ModuleConflict : DiagGroup<"module-conflict">;
607607
def ModuleFileExtension : DiagGroup<"module-file-extension">;
608608
def ModuleIncludeDirectiveTranslation : DiagGroup<"module-include-translation">;
609+
def ModuleMap : DiagGroup<"module-map">;
609610
def RoundTripCC1Args : DiagGroup<"round-trip-cc1-args">;
610611
def NewlineEOF : DiagGroup<"newline-eof">;
611612
def Nullability : DiagGroup<"nullability">;

clang/include/clang/Basic/DiagnosticLexKinds.td

+6
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,12 @@ def warn_pp_date_time : Warning<
838838
ShowInSystemHeader, DefaultIgnore, InGroup<DiagGroup<"date-time">>;
839839

840840
// Module map parsing
841+
def remark_mmap_parse : Remark<
842+
"parsing modulemap '%0'">, ShowInSystemHeader, InGroup<ModuleMap>;
843+
def remark_mmap_load : Remark<
844+
"loading modulemap '%0'">, ShowInSystemHeader, InGroup<ModuleMap>;
845+
def remark_mmap_load_module : Remark<
846+
"loading parsed module '%0'">, ShowInSystemHeader, InGroup<ModuleMap>;
841847
def err_mmap_unknown_token : Error<"skipping stray token">;
842848
def err_mmap_expected_module : Error<"expected module declaration">;
843849
def err_mmap_expected_module_name : Error<"expected module name">;

clang/include/clang/Lex/HeaderSearch.h

+45-25
Original file line numberDiff line numberDiff line change
@@ -332,13 +332,27 @@ class HeaderSearch {
332332
/// The mapping between modules and headers.
333333
mutable ModuleMap ModMap;
334334

335+
struct ModuleMapDirectoryState {
336+
OptionalFileEntryRef ModuleMapFile;
337+
enum {
338+
Parsed,
339+
Loaded,
340+
Invalid,
341+
} Status;
342+
};
343+
335344
/// Describes whether a given directory has a module map in it.
336-
llvm::DenseMap<const DirectoryEntry *, bool> DirectoryHasModuleMap;
345+
llvm::DenseMap<const DirectoryEntry *, ModuleMapDirectoryState>
346+
DirectoryModuleMap;
337347

338348
/// Set of module map files we've already loaded, and a flag indicating
339349
/// whether they were valid or not.
340350
llvm::DenseMap<const FileEntry *, bool> LoadedModuleMaps;
341351

352+
/// Set of module map files we've already parsed, and a flag indicating
353+
/// whether they were valid or not.
354+
llvm::DenseMap<const FileEntry *, bool> ParsedModuleMaps;
355+
342356
// A map of discovered headers with their associated include file name.
343357
llvm::DenseMap<const FileEntry *, llvm::SmallString<64>> IncludeNames;
344358

@@ -433,11 +447,6 @@ class HeaderSearch {
433447
/// Retrieve the path to the module cache.
434448
StringRef getModuleCachePath() const { return ModuleCachePath; }
435449

436-
/// Consider modules when including files from this directory.
437-
void setDirectoryHasModuleMap(const DirectoryEntry* Dir) {
438-
DirectoryHasModuleMap[Dir] = true;
439-
}
440-
441450
/// Forget everything we know about headers so far.
442451
void ClearFileInfo() {
443452
FileInfo.clear();
@@ -713,9 +722,10 @@ class HeaderSearch {
713722
/// used to resolve paths within the module (this is required when
714723
/// building the module from preprocessed source).
715724
/// \returns true if an error occurred, false otherwise.
716-
bool loadModuleMapFile(FileEntryRef File, bool IsSystem, FileID ID = FileID(),
717-
unsigned *Offset = nullptr,
718-
StringRef OriginalModuleMapFile = StringRef());
725+
bool parseAndLoadModuleMapFile(FileEntryRef File, bool IsSystem,
726+
FileID ID = FileID(),
727+
unsigned *Offset = nullptr,
728+
StringRef OriginalModuleMapFile = StringRef());
719729

720730
/// Collect the set of all known, top-level modules.
721731
///
@@ -915,26 +925,31 @@ class HeaderSearch {
915925
size_t getTotalMemory() const;
916926

917927
private:
918-
/// Describes what happened when we tried to load a module map file.
919-
enum LoadModuleMapResult {
920-
/// The module map file had already been loaded.
921-
LMM_AlreadyLoaded,
928+
/// Describes what happened when we tried to load or parse a module map file.
929+
enum ModuleMapResult {
930+
/// The module map file had already been processed.
931+
MMR_AlreadyProcessed,
922932

923-
/// The module map file was loaded by this invocation.
924-
LMM_NewlyLoaded,
933+
/// The module map file was processed by this invocation.
934+
MMR_NewlyProcessed,
925935

926936
/// There is was directory with the given name.
927-
LMM_NoDirectory,
937+
MMR_NoDirectory,
928938

929939
/// There was either no module map file or the module map file was
930940
/// invalid.
931-
LMM_InvalidModuleMap
941+
MMR_InvalidModuleMap
932942
};
933943

934-
LoadModuleMapResult loadModuleMapFileImpl(FileEntryRef File, bool IsSystem,
935-
DirectoryEntryRef Dir,
936-
FileID ID = FileID(),
937-
unsigned *Offset = nullptr);
944+
ModuleMapResult parseAndLoadModuleMapFileImpl(FileEntryRef File,
945+
bool IsSystem,
946+
DirectoryEntryRef Dir,
947+
FileID ID = FileID(),
948+
unsigned *Offset = nullptr);
949+
950+
ModuleMapResult parseModuleMapFileImpl(FileEntryRef File, bool IsSystem,
951+
DirectoryEntryRef Dir,
952+
FileID ID = FileID());
938953

939954
/// Try to load the module map file in the given directory.
940955
///
@@ -945,8 +960,8 @@ class HeaderSearch {
945960
///
946961
/// \returns The result of attempting to load the module map file from the
947962
/// named directory.
948-
LoadModuleMapResult loadModuleMapFile(StringRef DirName, bool IsSystem,
949-
bool IsFramework);
963+
ModuleMapResult parseAndLoadModuleMapFile(StringRef DirName, bool IsSystem,
964+
bool IsFramework);
950965

951966
/// Try to load the module map file in the given directory.
952967
///
@@ -956,8 +971,13 @@ class HeaderSearch {
956971
///
957972
/// \returns The result of attempting to load the module map file from the
958973
/// named directory.
959-
LoadModuleMapResult loadModuleMapFile(DirectoryEntryRef Dir, bool IsSystem,
960-
bool IsFramework);
974+
ModuleMapResult parseAndLoadModuleMapFile(DirectoryEntryRef Dir,
975+
bool IsSystem, bool IsFramework);
976+
977+
ModuleMapResult parseModuleMapFile(StringRef DirName, bool IsSystem,
978+
bool IsFramework);
979+
ModuleMapResult parseModuleMapFile(DirectoryEntryRef Dir, bool IsSystem,
980+
bool IsFramework);
961981
};
962982

963983
/// Apply the header search options to get given HeaderSearch object.

clang/include/clang/Lex/ModuleMap.h

+25-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/Basic/LangOptions.h"
1919
#include "clang/Basic/Module.h"
2020
#include "clang/Basic/SourceLocation.h"
21+
#include "clang/Lex/ModuleMapFile.h"
2122
#include "llvm/ADT/ArrayRef.h"
2223
#include "llvm/ADT/DenseMap.h"
2324
#include "llvm/ADT/DenseSet.h"
@@ -262,6 +263,18 @@ class ModuleMap {
262263
/// Describes whether we haved loaded a particular file as a module
263264
/// map.
264265
llvm::DenseMap<const FileEntry *, bool> LoadedModuleMap;
266+
llvm::DenseMap<const FileEntry *, const modulemap::ModuleMapFile *>
267+
ParsedModuleMap;
268+
269+
std::vector<std::unique_ptr<modulemap::ModuleMapFile>> ParsedModuleMaps;
270+
271+
/// Map from top level module name to a list of ModuleDecls in the order they
272+
/// were discovered. This allows handling shadowing correctly and diagnosing
273+
/// redefinitions.
274+
llvm::StringMap<SmallVector<std::pair<const modulemap::ModuleMapFile *,
275+
const modulemap::ModuleDecl *>,
276+
1>>
277+
ParsedModules;
265278

266279
/// Resolve the given export declaration into an actual export
267280
/// declaration.
@@ -478,6 +491,8 @@ class ModuleMap {
478491
/// \returns The named module, if known; otherwise, returns null.
479492
Module *findModule(StringRef Name) const;
480493

494+
Module *findOrLoadModule(StringRef Name);
495+
481496
Module *findOrInferSubmodule(Module *Parent, StringRef Name);
482497

483498
/// Retrieve a module with the given name using lexical name lookup,
@@ -693,6 +708,11 @@ class ModuleMap {
693708
void addHeader(Module *Mod, Module::Header Header,
694709
ModuleHeaderRole Role, bool Imported = false);
695710

711+
/// Parse a module map without creating `clang::Module` instances.
712+
bool parseModuleMapFile(FileEntryRef File, bool IsSystem,
713+
DirectoryEntryRef Dir, FileID ID = FileID(),
714+
SourceLocation ExternModuleLoc = SourceLocation());
715+
696716
/// Load the given module map file, and record any modules we
697717
/// encounter.
698718
///
@@ -713,10 +733,11 @@ class ModuleMap {
713733
/// that caused us to load this module map file, if any.
714734
///
715735
/// \returns true if an error occurred, false otherwise.
716-
bool loadModuleMapFile(FileEntryRef File, bool IsSystem,
717-
DirectoryEntryRef HomeDir, FileID ID = FileID(),
718-
unsigned *Offset = nullptr,
719-
SourceLocation ExternModuleLoc = SourceLocation());
736+
bool
737+
parseAndLoadModuleMapFile(FileEntryRef File, bool IsSystem,
738+
DirectoryEntryRef HomeDir, FileID ID = FileID(),
739+
unsigned *Offset = nullptr,
740+
SourceLocation ExternModuleLoc = SourceLocation());
720741

721742
/// Dump the contents of the module map, for debugging purposes.
722743
void dump();

clang/include/clang/Lex/ModuleMapFile.h

+9
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,17 @@ using TopLevelDecl = std::variant<ModuleDecl, ExternModuleDecl>;
133133
/// This holds many reference types (StringRef, SourceLocation, etc.) whose
134134
/// lifetimes are bound by the SourceManager and FileManager used.
135135
struct ModuleMapFile {
136+
/// The FileID used to parse this module map. This is always a local ID.
137+
FileID ID;
138+
139+
/// The directory in which the module map was discovered. Declarations in
140+
/// the module map are relative to this directory.
141+
OptionalDirectoryEntryRef Dir;
142+
136143
/// Beginning of the file, used for moduleMapFileRead callback.
137144
SourceLocation Start;
145+
146+
bool IsSystem;
138147
std::vector<TopLevelDecl> Decls;
139148

140149
void dump(llvm::raw_ostream &out) const;

clang/lib/Frontend/CompilerInstance.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -582,13 +582,13 @@ struct ReadModuleNames : ASTReaderListener {
582582
ModuleMap &MM = PP.getHeaderSearchInfo().getModuleMap();
583583
for (const std::string &LoadedModule : LoadedModules)
584584
MM.cacheModuleLoad(*PP.getIdentifierInfo(LoadedModule),
585-
MM.findModule(LoadedModule));
585+
MM.findOrLoadModule(LoadedModule));
586586
LoadedModules.clear();
587587
}
588588

589589
void markAllUnavailable() {
590590
for (const std::string &LoadedModule : LoadedModules) {
591-
if (Module *M = PP.getHeaderSearchInfo().getModuleMap().findModule(
591+
if (Module *M = PP.getHeaderSearchInfo().getModuleMap().findOrLoadModule(
592592
LoadedModule)) {
593593
M->HasIncompatibleModuleFile = true;
594594

clang/lib/Frontend/FrontendAction.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -621,8 +621,8 @@ static bool loadModuleMapForModuleBuild(CompilerInstance &CI, bool IsSystem,
621621
}
622622

623623
// Load the module map file.
624-
if (HS.loadModuleMapFile(*ModuleMap, IsSystem, ModuleMapID, &Offset,
625-
PresumedModuleMapFile))
624+
if (HS.parseAndLoadModuleMapFile(*ModuleMap, IsSystem, ModuleMapID, &Offset,
625+
PresumedModuleMapFile))
626626
return true;
627627

628628
if (SrcMgr.getBufferOrFake(ModuleMapID).getBufferSize() == Offset)
@@ -1077,8 +1077,8 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
10771077
// If we were asked to load any module map files, do so now.
10781078
for (const auto &Filename : CI.getFrontendOpts().ModuleMapFiles) {
10791079
if (auto File = CI.getFileManager().getOptionalFileRef(Filename))
1080-
CI.getPreprocessor().getHeaderSearchInfo().loadModuleMapFile(
1081-
*File, /*IsSystem*/false);
1080+
CI.getPreprocessor().getHeaderSearchInfo().parseAndLoadModuleMapFile(
1081+
*File, /*IsSystem*/ false);
10821082
else
10831083
CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename;
10841084
}

0 commit comments

Comments
 (0)