Skip to content

[clang][modules] Allow module maps with textual headers to be non-affecting #89441

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 24, 2024
10 changes: 6 additions & 4 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ GetAffectingModuleMaps(const Preprocessor &PP, Module *RootModule) {
continue;

const HeaderFileInfo *HFI = HS.getExistingLocalFileInfo(*File);
if (!HFI || (HFI->isModuleHeader && !HFI->isCompilingModuleHeader))
if (!HFI || ((HFI->isModuleHeader || HFI->isTextualModuleHeader) &&
!HFI->isCompilingModuleHeader))
continue;

for (const auto &KH : HS.findResolvedModulesForHeader(*File)) {
Expand Down Expand Up @@ -2055,11 +2056,12 @@ void ASTWriter::WriteHeaderSearch(const HeaderSearch &HS) {

// Get the file info. Skip emitting this file if we have no information on
// it as a header file (in which case HFI will be null) or if it hasn't
// changed since it was loaded. Also skip it if it's for a modular header
// from a different module; in that case, we rely on the module(s)
// changed since it was loaded. Also skip it if it's for a non-excluded
// header from a different module; in that case, we rely on the module(s)
// containing the header to provide this information.
const HeaderFileInfo *HFI = HS.getExistingLocalFileInfo(*File);
if (!HFI || (HFI->isModuleHeader && !HFI->isCompilingModuleHeader))
if (!HFI || ((HFI->isModuleHeader || HFI->isTextualModuleHeader) &&
!HFI->isCompilingModuleHeader))
continue;

// Massage the file path into an appropriate form.
Expand Down
26 changes: 26 additions & 0 deletions clang/test/Modules/prune-non-affecting-module-map-files-textual.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// This test checks that a module map with a textual header can be marked as
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a useful test, I think there are a couple of other affecting-ness properties that are important to test:

  • that a textual header that is included means its module is affecting (i.e. the include matters)
  • that this textual-affecting-ness doesn't leak outside the module (a module depending on B isn't affected by A.modulemap)

I tried to cover these in https://github.com/llvm/llvm-project/pull/89729/files#diff-2eed05f9a85bc5a79b9651ee0f23e5f1494e94a2f32e093847aa6dae5ce5d839 - it might be nice to add these as a second test here (I can do it as a followup if you prefer)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch unfortunately doesn't pass that test (on line 44) due to this: when computing affecting module maps for B, "A.h" makes it through the HFI filter, module A is added to ModulesToProcess and the module map for its import X is considered affecting.

I tried splitting up that logic so that we consider imports and undeclared uses only of the current module and its submodules. That doesn't work either, because PP.alreadyIncluded("X.h") returns true, since it checks the global (cross-module) state, not the local state. This might be possible with #71117. Until then, I think we need some "has this been included locally" state that's local to the current TU. Maybe that can live in HeaderFileInfo as per your patch, or as a Preprocessor state.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should work with b03c34f.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(And thanks for the extended test case!)

// non-affecting.

// RUN: rm -rf %t && mkdir %t
// RUN: split-file %s %t

//--- A.modulemap
module A { textual header "A.h" }
//--- B.modulemap
module B { header "B.h" export * }
//--- A.h
typedef int A_int;
//--- B.h
#include "A.h"
typedef A_int B_int;

// RUN: %clang_cc1 -fmodules -emit-module %t/A.modulemap -fmodule-name=A -o %t/A.pcm \
// RUN: -fmodule-map-file=%t/A.modulemap -fmodule-map-file=%t/B.modulemap

// RUN: %clang_cc1 -fmodules -emit-module %t/B.modulemap -fmodule-name=B -o %t/B0.pcm \
// RUN: -fmodule-map-file=%t/A.modulemap -fmodule-map-file=%t/B.modulemap -fmodule-file=%t/A.pcm

// RUN: %clang_cc1 -fmodules -emit-module %t/B.modulemap -fmodule-name=B -o %t/B1.pcm \
// RUN: -fmodule-map-file=%t/B.modulemap -fmodule-file=%t/A.pcm

// RUN: diff %t/B0.pcm %t/B1.pcm