Skip to content

Commit bd3dde0

Browse files
authored
[clang][Modules] Diagnose mismatching pcm dependencies in explicit buiilds (#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.
1 parent 5528770 commit bd3dde0

File tree

3 files changed

+92
-5
lines changed

3 files changed

+92
-5
lines changed

clang/include/clang/Basic/DiagnosticSerializationKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ def warn_decls_in_multiple_modules : Warning<
139139
InGroup<DiagGroup<"decls-in-multiple-modules">>,
140140
DefaultIgnore;
141141

142+
def warn_module_file_mapping_mismatch
143+
: Warning<"loaded module file '%0' conflicts with imported file '%1'">,
144+
InGroup<DiagGroup<"module-file-mapping-mismatch">>,
145+
DefaultIgnore;
146+
142147
def err_failed_to_find_module_file : Error<
143148
"failed to find module file for module '%0'">;
144149
} // let CategoryName

clang/lib/Serialization/ASTReader.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3286,6 +3286,8 @@ ASTReader::ReadControlBlock(ModuleFile &F,
32863286
time_t StoredModTime = 0;
32873287
ASTFileSignature StoredSignature;
32883288
std::string ImportedFile;
3289+
std::string StoredFile;
3290+
bool IgnoreImportedByNote = false;
32893291

32903292
// For prebuilt and explicit modules first consult the file map for
32913293
// an override. Note that here we don't search prebuilt module
@@ -3311,11 +3313,26 @@ ASTReader::ReadControlBlock(ModuleFile &F,
33113313
SignatureBytes.end());
33123314
Blob = Blob.substr(ASTFileSignature::size);
33133315

3316+
// Use BaseDirectoryAsWritten to ensure we use the same path in the
3317+
// ModuleCache as when writing.
3318+
StoredFile = ReadPathBlob(BaseDirectoryAsWritten, Record, Idx, Blob);
33143319
if (ImportedFile.empty()) {
3315-
// Use BaseDirectoryAsWritten to ensure we use the same path in the
3316-
// ModuleCache as when writing.
3317-
ImportedFile =
3318-
ReadPathBlob(BaseDirectoryAsWritten, Record, Idx, Blob);
3320+
ImportedFile = StoredFile;
3321+
} else if (!getDiags().isIgnored(
3322+
diag::warn_module_file_mapping_mismatch,
3323+
CurrentImportLoc)) {
3324+
auto ImportedFileRef =
3325+
PP.getFileManager().getOptionalFileRef(ImportedFile);
3326+
auto StoredFileRef =
3327+
PP.getFileManager().getOptionalFileRef(StoredFile);
3328+
if ((ImportedFileRef && StoredFileRef) &&
3329+
(*ImportedFileRef != *StoredFileRef)) {
3330+
Diag(diag::warn_module_file_mapping_mismatch)
3331+
<< ImportedFile << StoredFile;
3332+
Diag(diag::note_module_file_imported_by)
3333+
<< F.FileName << !F.ModuleName.empty() << F.ModuleName;
3334+
IgnoreImportedByNote = true;
3335+
}
33193336
}
33203337
}
33213338

@@ -3349,7 +3366,8 @@ ASTReader::ReadControlBlock(ModuleFile &F,
33493366
.getModuleCache()
33503367
.getInMemoryModuleCache()
33513368
.isPCMFinal(F.FileName);
3352-
if (isDiagnosedResult(Result, Capabilities) || recompilingFinalized)
3369+
if (!IgnoreImportedByNote &&
3370+
(isDiagnosedResult(Result, Capabilities) || recompilingFinalized))
33533371
Diag(diag::note_module_file_imported_by)
33543372
<< F.FileName << !F.ModuleName.empty() << F.ModuleName;
33553373

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/// This tests the expected error case when there is a mismatch between the pcm dependencies passed in
2+
/// the command line with `fmodule-file` and whats encoded in the pcm.
3+
4+
/// The steps are:
5+
/// 1. Build the module A with no dependencies. The first variant to build is A-1.pcm.
6+
/// 2. Build the same module with files that resolve from different search paths.
7+
/// This variant is named A-2.pcm.
8+
/// 3. Build module B that depends on the earlier module A-1.pcm.
9+
/// 4. Build client that directly depends on both modules (A & B),
10+
/// but depends on a incompatible variant of A (A-2.pcm) for B to use.
11+
12+
// RUN: rm -rf %t
13+
// RUN: split-file %s %t
14+
15+
// RUN: %clang_cc1 -emit-module -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \
16+
// RUN: -I%t/Sysroot/usr/include \
17+
// RUN: -fmodule-name=A %t/Sysroot/usr/include/A/module.modulemap -o %t/A-1.pcm
18+
19+
// RUN: %clang_cc1 -emit-module -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \
20+
// RUN: -I%t/BuildDir \
21+
// RUN: -fmodule-name=A %t/BuildDir/A/module.modulemap -o %t/A-2.pcm
22+
23+
// RUN: %clang_cc1 -emit-module -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \
24+
// RUN: -I%t/Sysroot/usr/include \
25+
// RUN: -fmodule-map-file=%t/Sysroot/usr/include/A/module.modulemap \
26+
// RUN: -fmodule-file=A=%t/A-1.pcm \
27+
// RUN: -fmodule-name=B %t/Sysroot/usr/include/B/module.modulemap -o %t/B-1.pcm
28+
29+
// RUN: %clang_cc1 -x c -fmodules -fno-implicit-modules -isysroot %t/Sysroot \
30+
// RUN: -I%t/BuildDir -I%t/Sysroot/usr/include \
31+
// RUN: -fmodule-map-file=%t/BuildDir/A/module.modulemap \
32+
// RUN: -fmodule-map-file=%t/Sysroot/usr/include/B/module.modulemap \
33+
// RUN: -fmodule-file=A=%t/A-2.pcm -fmodule-file=B=%t/B-1.pcm \
34+
// RUN: -Wmodule-file-mapping-mismatch -verify %s
35+
36+
37+
#include <A/A.h>
38+
#include <B/B.h> // expected-warning {{conflicts with imported file}} \
39+
// expected-note {{imported by module 'B'}} \
40+
// expected-error {{out of date and needs to be rebuilt}}
41+
42+
//--- Sysroot/usr/include/A/module.modulemap
43+
module A [system] {
44+
umbrella header "A.h"
45+
}
46+
//--- Sysroot/usr/include/A/A.h
47+
typedef int A_t;
48+
49+
//--- Sysroot/usr/include/B/module.modulemap
50+
module B [system] {
51+
umbrella header "B.h"
52+
}
53+
54+
//--- Sysroot/usr/include/B/B.h
55+
#include <A/A.h>
56+
typedef int B_t;
57+
58+
//--- BuildDir/A/module.modulemap
59+
module A [system] {
60+
umbrella header "A.h"
61+
}
62+
63+
//--- BuildDir/A/A.h
64+
typedef int A_t;

0 commit comments

Comments
 (0)