Skip to content

[clang][Modules] Diagnose mismatching pcm dependencies in explicit buiilds #137068

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 3 commits into from
Apr 29, 2025

Conversation

cyndyishida
Copy link
Member

In an explicit build, the dependency scanner generates invocations with dependencies to module files to use during compilation. The pcm's passed in the invocations should match the ones that were imported by other modules that share the same dependencies.

We have seen bugs caused from incorrect invocations that mismatch which module file to load. When this happens report it as a warning, to help with investigations, as that should never occur in a well behaved build scheduled from the dependency scanner.

The warning flag is off by default.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules labels Apr 23, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 23, 2025

@llvm/pr-subscribers-clang-modules

@llvm/pr-subscribers-clang

Author: Cyndy Ishida (cyndyishida)

Changes

In an explicit build, the dependency scanner generates invocations with dependencies to module files to use during compilation. The pcm's passed in the invocations should match the ones that were imported by other modules that share the same dependencies.

We have seen bugs caused from incorrect invocations that mismatch which module file to load. When this happens report it as a warning, to help with investigations, as that should never occur in a well behaved build scheduled from the dependency scanner.

The warning flag is off by default.


Full diff: https://github.com/llvm/llvm-project/pull/137068.diff

3 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticSerializationKinds.td (+5)
  • (modified) clang/lib/Serialization/ASTReader.cpp (+16-4)
  • (added) clang/test/Modules/invalid-module-dep.c (+65)
diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td
index 5fc5937b80d35..1d024fcf38a41 100644
--- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td
@@ -139,6 +139,11 @@ def warn_decls_in_multiple_modules : Warning<
   InGroup<DiagGroup<"decls-in-multiple-modules">>,
   DefaultIgnore;
 
+def warn_lazy_pcm_mismatch
+    : Warning<"loaded module file '%0' conflicts with file discovered '%1'">,
+      InGroup<DiagGroup<"lazy-pcm-mismatch">>,
+      DefaultIgnore;
+
 def err_failed_to_find_module_file : Error<
   "failed to find module file for module '%0'">;
 } // let CategoryName
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 70b54b7296882..5761dc09a3e59 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -3286,6 +3286,7 @@ ASTReader::ReadControlBlock(ModuleFile &F,
       time_t StoredModTime = 0;
       ASTFileSignature StoredSignature;
       std::string ImportedFile;
+      std::string StoredFile;
 
       // For prebuilt and explicit modules first consult the file map for
       // an override. Note that here we don't search prebuilt module
@@ -3311,11 +3312,22 @@ ASTReader::ReadControlBlock(ModuleFile &F,
                                                    SignatureBytes.end());
         Blob = Blob.substr(ASTFileSignature::size);
 
+        // Use BaseDirectoryAsWritten to ensure we use the same path in the
+        // ModuleCache as when writing.
+        StoredFile = ReadPathBlob(BaseDirectoryAsWritten, Record, Idx, Blob);
         if (ImportedFile.empty()) {
-          // Use BaseDirectoryAsWritten to ensure we use the same path in the
-          // ModuleCache as when writing.
-          ImportedFile =
-              ReadPathBlob(BaseDirectoryAsWritten, Record, Idx, Blob);
+          ImportedFile = StoredFile;
+        } else {
+          auto ImportedFileRef =
+              PP.getFileManager().getOptionalFileRef(ImportedFile);
+          auto StoredFileRef =
+              PP.getFileManager().getOptionalFileRef(StoredFile);
+          if ((ImportedFileRef && StoredFileRef) &&
+              (*ImportedFileRef != *StoredFileRef)) {
+            Diag(diag::warn_lazy_pcm_mismatch) << ImportedFile << StoredFile;
+            Diag(diag::note_module_file_imported_by)
+                << F.FileName << !F.ModuleName.empty() << F.ModuleName;
+          }
         }
       }
 
diff --git a/clang/test/Modules/invalid-module-dep.c b/clang/test/Modules/invalid-module-dep.c
new file mode 100644
index 0000000000000..7c74730f87c0c
--- /dev/null
+++ b/clang/test/Modules/invalid-module-dep.c
@@ -0,0 +1,65 @@
+/// This tests the expected error case when there is a mismatch between the pcm dependencies passed in 
+/// the command line with `fmodule-file` and whats encoded in the pcm. 
+
+/// The steps are: 
+/// 1. Build the module A with no dependencies. The first variant to build is A-1.pcm.
+/// 2. Build the same module with files that resolve from different search paths. 
+///     This variant is named A-2.pcm.
+/// 3. Build module B that depends on the earlier module A-1.pcm.
+/// 4. Build client that directly depends on both modules (A & B), 
+///     but depends on a incompatible variant of A (A-2.pcm) for B to use.
+
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -emit-module -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \
+// RUN:     -I%t/Sysroot/usr/include \
+// RUN:     -fmodule-name=A %t/Sysroot/usr/include/A/module.modulemap -o %t/A-1.pcm
+
+// RUN: %clang_cc1 -emit-module -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \
+// RUN:     -I%t/BuildDir \
+// RUN:     -fmodule-name=A %t/BuildDir/A/module.modulemap -o %t/A-2.pcm
+
+// RUN: %clang_cc1 -emit-module -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \
+// RUN:     -I%t/Sysroot/usr/include \
+// RUN:     -fmodule-map-file=%t/Sysroot/usr/include/A/module.modulemap \
+// RUN:     -fmodule-file=A=%t/A-1.pcm \
+// RUN:     -fmodule-name=B %t/Sysroot/usr/include/B/module.modulemap -o %t/B-1.pcm
+
+// RUN: %clang_cc1 -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \
+// RUN:     -I%t/BuildDir -I%t/Sysroot/usr/include \
+// RUN:     -fmodule-map-file=%t/BuildDir/A/module.modulemap \
+// RUN:     -fmodule-map-file=%t/Sysroot/usr/include/B/module.modulemap \
+// RUN:     -fmodule-file=A=%t/A-2.pcm -fmodule-file=B=%t/B-1.pcm \
+// RUN:     -Wlazy-pcm-mismatch -verify %s  
+
+
+#include <A/A.h>
+#include <B/B.h> // expected-warning {{conflicts with file discovered}} \
+                 // expected-note {{imported by module 'B'}} \
+                 // expected-error {{out of date and needs to be rebuilt}} \
+                 // expected-note {{imported by module 'B'}}
+
+//--- Sysroot/usr/include/A/module.modulemap
+module A [system] {
+  umbrella header "A.h"
+}
+//--- Sysroot/usr/include/A/A.h
+typedef int A_t;
+
+//--- Sysroot/usr/include/B/module.modulemap
+module B [system] {
+  umbrella header "B.h"
+}
+
+//--- Sysroot/usr/include/B/B.h
+#include <A/A.h>
+typedef int B_t;
+
+//--- BuildDir/A/module.modulemap
+module A [system] {
+  umbrella header "A.h"
+}
+
+//--- BuildDir/A/A.h
+typedef int A_t;

…ilds

In an explicit build, the dependency scanner generates invocations with
dependencies to module files to use during compilation. The pcm's passed
in the invocations should match the ones that were imported by other
modules that share the same dependencies.

We have seen bugs caused from incorrect invocations that mismatch which module file to load.
When this happens report it as a warning, to help with investigations, as
that should never occur in a well behaved build scheduled from the
dependency scanner.

The warning flag is off by default.
@cyndyishida cyndyishida force-pushed the eng/PR-149479912Attempt2 branch from 738bcb3 to bb548cb Compare April 23, 2025 22:06
Copy link
Contributor

@jansvoboda11 jansvoboda11 left a comment

Choose a reason for hiding this comment

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

LGTM, but I'd try to align the spelling of the diagnostic ("-Wlazy-pcm-mismatch") with how we talk about the -fmodule-file=X=X.pcm option ("mapping of module name to precompiled module file"). Maybe "-Wmodule-file-mapping-mismatch" or something similar?

@cyndyishida cyndyishida merged commit bd3dde0 into llvm:main Apr 29, 2025
6 of 10 checks passed
@cyndyishida cyndyishida deleted the eng/PR-149479912Attempt2 branch April 29, 2025 15:38
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…iilds (llvm#137068)

In an explicit build, the dependency scanner generates invocations with
dependencies to module files to use during compilation. The pcm's passed
in the invocations should match the ones that were imported by other
modules that share the same dependencies.

We have seen bugs caused from incorrect invocations that mismatch which
module file to load. When this happens report it as a warning, to help
with investigations, as that should never occur in a well behaved build
scheduled from the dependency scanner.

The warning flag is off by default.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…iilds (llvm#137068)

In an explicit build, the dependency scanner generates invocations with
dependencies to module files to use during compilation. The pcm's passed
in the invocations should match the ones that were imported by other
modules that share the same dependencies.

We have seen bugs caused from incorrect invocations that mismatch which
module file to load. When this happens report it as a warning, to help
with investigations, as that should never occur in a well behaved build
scheduled from the dependency scanner.

The warning flag is off by default.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…iilds (llvm#137068)

In an explicit build, the dependency scanner generates invocations with
dependencies to module files to use during compilation. The pcm's passed
in the invocations should match the ones that were imported by other
modules that share the same dependencies.

We have seen bugs caused from incorrect invocations that mismatch which
module file to load. When this happens report it as a warning, to help
with investigations, as that should never occur in a well behaved build
scheduled from the dependency scanner.

The warning flag is off by default.
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
…iilds (llvm#137068)

In an explicit build, the dependency scanner generates invocations with
dependencies to module files to use during compilation. The pcm's passed
in the invocations should match the ones that were imported by other
modules that share the same dependencies.

We have seen bugs caused from incorrect invocations that mismatch which
module file to load. When this happens report it as a warning, to help
with investigations, as that should never occur in a well behaved build
scheduled from the dependency scanner.

The warning flag is off by default.
Ankur-0429 pushed a commit to Ankur-0429/llvm-project that referenced this pull request May 9, 2025
…iilds (llvm#137068)

In an explicit build, the dependency scanner generates invocations with
dependencies to module files to use during compilation. The pcm's passed
in the invocations should match the ones that were imported by other
modules that share the same dependencies.

We have seen bugs caused from incorrect invocations that mismatch which
module file to load. When this happens report it as a warning, to help
with investigations, as that should never occur in a well behaved build
scheduled from the dependency scanner.

The warning flag is off by default.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants