Skip to content

Commit 85df42b

Browse files
[clang][modules] HeaderSearch::MarkFileModuleHeader sets textual headers' HeaderFileInfo non-external when it shouldn't
HeaderSearch::MarkFileModuleHeader is no longer properly checking for no-changes, and so sets the HeaderFileInfo for every `textual header` to non-external.
1 parent e7d5635 commit 85df42b

File tree

3 files changed

+81
-4
lines changed

3 files changed

+81
-4
lines changed

clang/include/clang/Lex/HeaderSearch.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ struct HeaderFileInfo {
9090
LLVM_PREFERRED_TYPE(bool)
9191
unsigned isModuleHeader : 1;
9292

93-
/// Whether this header is a `textual header` in a module.
93+
/// Whether this header is a `textual header` in a module. If a header is
94+
/// textual in one module and normal in another module, this bit will not be
95+
/// set, only `isModuleHeader`.
9496
LLVM_PREFERRED_TYPE(bool)
9597
unsigned isTextualModuleHeader : 1;
9698

clang/lib/Lex/HeaderSearch.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,11 +1313,18 @@ OptionalFileEntryRef HeaderSearch::LookupSubframeworkHeader(
13131313
// File Info Management.
13141314
//===----------------------------------------------------------------------===//
13151315

1316+
static bool moduleMembershipNeedsMerge(const HeaderFileInfo *HFI,
1317+
ModuleMap::ModuleHeaderRole Role) {
1318+
if (ModuleMap::isModular(Role))
1319+
return !HFI->isModuleHeader || HFI->isTextualModuleHeader;
1320+
if (!HFI->isModuleHeader && (Role & ModuleMap::TextualHeader))
1321+
return !HFI->isTextualModuleHeader;
1322+
return false;
1323+
}
1324+
13161325
static void mergeHeaderFileInfoModuleBits(HeaderFileInfo &HFI,
13171326
bool isModuleHeader,
13181327
bool isTextualModuleHeader) {
1319-
assert((!isModuleHeader || !isTextualModuleHeader) &&
1320-
"A header can't build with a module and be textual at the same time");
13211328
HFI.isModuleHeader |= isModuleHeader;
13221329
if (HFI.isModuleHeader)
13231330
HFI.isTextualModuleHeader = false;
@@ -1432,7 +1439,7 @@ void HeaderSearch::MarkFileModuleHeader(FileEntryRef FE,
14321439
if ((Role & ModuleMap::ExcludedHeader))
14331440
return;
14341441
auto *HFI = getExistingFileInfo(FE);
1435-
if (HFI && HFI->isModuleHeader)
1442+
if (HFI && !moduleMembershipNeedsMerge(HFI, Role))
14361443
return;
14371444
}
14381445

clang/unittests/Lex/HeaderSearchTest.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,5 +323,73 @@ TEST_F(HeaderSearchTest, HeaderMapFrameworkLookup) {
323323
EXPECT_EQ(Search.getIncludeNameForHeader(FE), "Foo/Foo.h");
324324
}
325325

326+
TEST_F(HeaderSearchTest, HeaderFileInfoMerge) {
327+
auto AddHeader = [&](std::string HeaderPath) -> FileEntryRef {
328+
VFS->addFile(HeaderPath, 0,
329+
llvm::MemoryBuffer::getMemBufferCopy("", HeaderPath),
330+
/*User=*/std::nullopt, /*Group=*/std::nullopt,
331+
llvm::sys::fs::file_type::regular_file);
332+
return *FileMgr.getOptionalFileRef(HeaderPath);
333+
};
334+
335+
class MockExternalHeaderFileInfoSource : public ExternalHeaderFileInfoSource {
336+
HeaderFileInfo GetHeaderFileInfo(FileEntryRef FE) {
337+
HeaderFileInfo HFI;
338+
auto FileName = FE.getName();
339+
if (FileName == ModularPath)
340+
HFI.mergeModuleMembership(ModuleMap::NormalHeader);
341+
else if (FileName == TextualPath)
342+
HFI.mergeModuleMembership(ModuleMap::TextualHeader);
343+
HFI.External = true;
344+
HFI.IsValid = true;
345+
return HFI;
346+
}
347+
348+
public:
349+
std::string ModularPath = "/modular.h";
350+
std::string TextualPath = "/textual.h";
351+
};
352+
353+
auto ExternalSource = new MockExternalHeaderFileInfoSource();
354+
Search.SetExternalSource(ExternalSource);
355+
356+
// Everything should start out external.
357+
auto ModularFE = AddHeader(ExternalSource->ModularPath);
358+
auto TextualFE = AddHeader(ExternalSource->TextualPath);
359+
EXPECT_TRUE(Search.getExistingFileInfo(ModularFE)->External);
360+
EXPECT_TRUE(Search.getExistingFileInfo(TextualFE)->External);
361+
362+
// Marking the same role should keep it external
363+
Search.MarkFileModuleHeader(ModularFE, ModuleMap::NormalHeader,
364+
/*isCompilingModuleHeader=*/false);
365+
Search.MarkFileModuleHeader(TextualFE, ModuleMap::TextualHeader,
366+
/*isCompilingModuleHeader=*/false);
367+
EXPECT_TRUE(Search.getExistingFileInfo(ModularFE)->External);
368+
EXPECT_TRUE(Search.getExistingFileInfo(TextualFE)->External);
369+
370+
// textual -> modular should update the HFI, but modular -> textual should be
371+
// a no-op.
372+
Search.MarkFileModuleHeader(ModularFE, ModuleMap::TextualHeader,
373+
/*isCompilingModuleHeader=*/false);
374+
Search.MarkFileModuleHeader(TextualFE, ModuleMap::NormalHeader,
375+
/*isCompilingModuleHeader=*/false);
376+
auto ModularFI = Search.getExistingFileInfo(ModularFE);
377+
auto TextualFI = Search.getExistingFileInfo(TextualFE);
378+
EXPECT_TRUE(ModularFI->External);
379+
EXPECT_TRUE(ModularFI->isModuleHeader);
380+
EXPECT_FALSE(ModularFI->isTextualModuleHeader);
381+
EXPECT_FALSE(TextualFI->External);
382+
EXPECT_TRUE(TextualFI->isModuleHeader);
383+
EXPECT_FALSE(TextualFI->isTextualModuleHeader);
384+
385+
// Compiling the module should make the HFI local.
386+
Search.MarkFileModuleHeader(ModularFE, ModuleMap::NormalHeader,
387+
/*isCompilingModuleHeader=*/true);
388+
Search.MarkFileModuleHeader(TextualFE, ModuleMap::NormalHeader,
389+
/*isCompilingModuleHeader=*/true);
390+
EXPECT_FALSE(Search.getExistingFileInfo(ModularFE)->External);
391+
EXPECT_FALSE(Search.getExistingFileInfo(TextualFE)->External);
392+
}
393+
326394
} // namespace
327395
} // namespace clang

0 commit comments

Comments
 (0)