Skip to content

Commit be38130

Browse files
[cherry-pick stable/20240408][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. rdar://129866498
1 parent b5a01ec commit be38130

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
@@ -84,7 +84,9 @@ struct HeaderFileInfo {
8484
LLVM_PREFERRED_TYPE(bool)
8585
unsigned isModuleHeader : 1;
8686

87-
/// Whether this header is a `textual header` in a module.
87+
/// Whether this header is a `textual header` in a module. If a header is
88+
/// textual in one module and normal in another module, this bit will not be
89+
/// set, only `isModuleHeader`.
8890
LLVM_PREFERRED_TYPE(bool)
8991
unsigned isTextualModuleHeader : 1;
9092

clang/lib/Lex/HeaderSearch.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,11 +1307,18 @@ OptionalFileEntryRef HeaderSearch::LookupSubframeworkHeader(
13071307
// File Info Management.
13081308
//===----------------------------------------------------------------------===//
13091309

1310+
static bool moduleMembershipNeedsMerge(const HeaderFileInfo *HFI,
1311+
ModuleMap::ModuleHeaderRole Role) {
1312+
if (ModuleMap::isModular(Role))
1313+
return !HFI->isModuleHeader || HFI->isTextualModuleHeader;
1314+
if (!HFI->isModuleHeader && (Role & ModuleMap::TextualHeader))
1315+
return !HFI->isTextualModuleHeader;
1316+
return false;
1317+
}
1318+
13101319
static void mergeHeaderFileInfoModuleBits(HeaderFileInfo &HFI,
13111320
bool isModuleHeader,
13121321
bool isTextualModuleHeader) {
1313-
assert((!isModuleHeader || !isTextualModuleHeader) &&
1314-
"A header can't build with a module and be textual at the same time");
13151322
HFI.isModuleHeader |= isModuleHeader;
13161323
if (HFI.isModuleHeader)
13171324
HFI.isTextualModuleHeader = false;
@@ -1426,7 +1433,7 @@ void HeaderSearch::MarkFileModuleHeader(FileEntryRef FE,
14261433
if ((Role & ModuleMap::ExcludedHeader))
14271434
return;
14281435
auto *HFI = getExistingFileInfo(FE);
1429-
if (HFI && HFI->isModuleHeader)
1436+
if (HFI && !moduleMembershipNeedsMerge(HFI, Role))
14301437
return;
14311438
}
14321439

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(TextualFI->isModuleHeader);
368+
EXPECT_FALSE(TextualFI->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)