Skip to content

Commit eb296a4

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 602634d commit eb296a4

File tree

3 files changed

+82
-4
lines changed

3 files changed

+82
-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: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,11 +1313,19 @@ 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+
else if (!HFI->isModuleHeader && (Role & ModuleMap::TextualHeader))
1321+
return !HFI->isTextualModuleHeader;
1322+
else
1323+
return false;
1324+
}
1325+
13161326
static void mergeHeaderFileInfoModuleBits(HeaderFileInfo &HFI,
13171327
bool isModuleHeader,
13181328
bool isTextualModuleHeader) {
1319-
assert((!isModuleHeader || !isTextualModuleHeader) &&
1320-
"A header can't build with a module and be textual at the same time");
13211329
HFI.isModuleHeader |= isModuleHeader;
13221330
if (HFI.isModuleHeader)
13231331
HFI.isTextualModuleHeader = false;
@@ -1432,7 +1440,7 @@ void HeaderSearch::MarkFileModuleHeader(FileEntryRef FE,
14321440
if ((Role & ModuleMap::ExcludedHeader))
14331441
return;
14341442
auto *HFI = getExistingFileInfo(FE);
1435-
if (HFI && HFI->isModuleHeader)
1443+
if (HFI && !moduleMembershipNeedsMerge(HFI, Role))
14361444
return;
14371445
}
14381446

clang/unittests/Lex/HeaderSearchTest.cpp

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

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

0 commit comments

Comments
 (0)