[clang] [C++20][Modules] Omit imported PCM file info for explicitly mapped header units (PR #235597)
Michael Park via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 25 23:38:23 PDT 2026
https://github.com/mpark updated https://github.com/llvm/llvm-project/pull/235597
>From fb470ada3fe787dae7c02711f575df83a0adc9e5 Mon Sep 17 00:00:00 2001
From: Michael Park <mcypark at gmail.com>
Date: Sun, 25 Oct 2026 21:30:05 -0700
Subject: [PATCH] [C++20][Modules] Omit imported PCM file info for explicitly
mapped header units
C++20 named module dependencies are already serialized without PCM file
information. This PR applies the same behavior to header units loaded through
a named mapping (`-fmodule-file=<name>=<pcm>`). Header units loaded through
`-fmodule-file=<file>` retain their file information.
An explicit `IsCXX20ModuleOrPrebuiltHeaderUnit` field is added to
the `IMPORT` record rather than reusing `StandardCXXModule`, which
also controls imported-module lookup.
This also adds a missing test for the existing named-module behavior and
tests the corresponding header-unit behavior introduced by this change.
---
clang/lib/Serialization/ASTReader.cpp | 24 +++---
clang/lib/Serialization/ASTWriter.cpp | 19 ++++-
clang/lib/Serialization/GlobalModuleIndex.cpp | 8 +-
clang/test/Modules/cxx20-import-paths.cpp | 80 +++++++++++++++++++
4 files changed, 116 insertions(+), 15 deletions(-)
create mode 100644 clang/test/Modules/cxx20-import-paths.cpp
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 56f1aa8a7f932a..cd7d9f24924f82 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -3538,6 +3538,7 @@ ASTReader::ReadControlBlock(ModuleFile &F,
StringRef ImportedName = ReadStringBlob(Record, Idx, Blob);
bool IsImportingStdCXXModule = Record[Idx++];
+ bool IsCXX20ModuleOrPrebuiltHeaderUnit = Record[Idx++];
off_t StoredSize = 0;
time_t StoredModTime = 0;
@@ -3557,12 +3558,12 @@ ASTReader::ReadControlBlock(ModuleFile &F,
ImportedFile = PP.getHeaderSearchInfo().getPrebuiltModuleFileName(
ImportedName, /*FileMapOnly*/ !IsImportingStdCXXModule);
- if (IsImportingStdCXXModule && ImportedFile.empty()) {
+ if (IsCXX20ModuleOrPrebuiltHeaderUnit && ImportedFile.empty()) {
Diag(diag::err_failed_to_find_module_file) << ImportedName;
return Missing;
}
- if (!IsImportingStdCXXModule) {
+ if (!IsCXX20ModuleOrPrebuiltHeaderUnit) {
StoredSize = (off_t)Record[Idx++];
StoredModTime = (time_t)Record[Idx++];
FileNameKind = (unsigned)Record[Idx++];
@@ -3604,10 +3605,12 @@ ASTReader::ReadControlBlock(ModuleFile &F,
Loaded, StoredSize, StoredModTime,
StoredSignature, Capabilities);
- // Check the AST we just read from ImportedFile contains a different
- // module than we expected (ImportedName). This can occur for C++20
- // Modules when given a mismatch via -fmodule-file=<name>=<file>
- if (IsImportingStdCXXModule) {
+ // Check whether the AST we just read from ImportedFile contains a
+ // different module than we expected (ImportedName).
+ //
+ // This can occur for C++20 Modules (including explicitly mapped header
+ // units) when given a mismatch in -fmodule-file=<name>=<file>.
+ if (IsCXX20ModuleOrPrebuiltHeaderUnit) {
if (const auto *Imported =
getModuleManager().lookupByFileName(ImportedFile);
Imported != nullptr && Imported->ModuleName != ImportedName) {
@@ -6329,11 +6332,12 @@ bool ASTReader::readASTFileControlBlock(
StringRef ModuleName = ReadStringBlob(Record, Idx, Blob);
- bool IsStandardCXXModule = Record[Idx++];
+ ++Idx; // Standard C++ module
+ bool IsCXX20ModuleOrPrebuiltHeaderUnit = Record[Idx++];
- // In C++20 Modules, we don't record the path to imported
- // modules in the BMI files.
- if (IsStandardCXXModule) {
+ // For C++20 Modules (including explicitly mapped header units),
+ // we don't record the path to imported modules in the BMI files.
+ if (IsCXX20ModuleOrPrebuiltHeaderUnit) {
Listener.visitImport(ModuleName, /*Filename=*/"");
continue;
}
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 0d9ec7a460dfbf..63ce3023964a86 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -1578,12 +1578,14 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) {
// Imports
if (Chain) {
+ const auto &HSOpts = PP.getHeaderSearchInfo().getHeaderSearchOpts();
auto Abbrev = std::make_shared<BitCodeAbbrev>();
Abbrev->Add(BitCodeAbbrevOp(IMPORT));
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Kind
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // ImportLoc
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Module name len
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // Standard C++ mod
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // C++20/prebuilt HU
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File size
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File timestamp
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // File name raw kind
@@ -1607,9 +1609,20 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) {
AddStringBlob(M.ModuleName, Record, Blob);
Record.push_back(M.StandardCXXModule);
- // We don't want to hard code the information about imported modules
- // in the C++20 named modules.
- if (M.StandardCXXModule) {
+ // We don't want to hard code file information for C++20 Modules
+ // (including explicitly mapped header units).
+ const Module *ImportedModule =
+ PP.getHeaderSearchInfo().getModuleMap().findModule(M.ModuleName);
+ bool IsPrebuiltHeaderUnit =
+ ImportedModule && ImportedModule->isHeaderUnit() &&
+ M.Kind == MK_PrebuiltModule &&
+ HSOpts.PrebuiltModuleFiles.count(M.ModuleName);
+
+ bool IsCXX20ModuleOrPrebuiltHeaderUnit =
+ M.StandardCXXModule || IsPrebuiltHeaderUnit;
+ Record.push_back(IsCXX20ModuleOrPrebuiltHeaderUnit);
+
+ if (IsCXX20ModuleOrPrebuiltHeaderUnit) {
Record.push_back(0);
Record.push_back(0);
Record.push_back(0);
diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp
index 6d0678141fa73a..7ab9a3d299f649 100644
--- a/clang/lib/Serialization/GlobalModuleIndex.cpp
+++ b/clang/lib/Serialization/GlobalModuleIndex.cpp
@@ -630,8 +630,12 @@ llvm::Error GlobalModuleIndexBuilder::loadModuleFile(FileEntryRef File) {
// modules while here we are only dealing with cached).
Blob = Blob.substr(Record[Idx++]);
- // Skip if it is standard C++ module
- ++Idx;
+ ++Idx; // Standard C++ module
+ bool IsCXX20ModuleOrPrebuiltHeaderUnit = Record[Idx++];
+
+ // Skip C++20 modules and prebuilt header units.
+ if (IsCXX20ModuleOrPrebuiltHeaderUnit)
+ continue;
// Load stored size/modification time.
off_t StoredSize = (off_t)Record[Idx++];
diff --git a/clang/test/Modules/cxx20-import-paths.cpp b/clang/test/Modules/cxx20-import-paths.cpp
new file mode 100644
index 00000000000000..d535d84009eba2
--- /dev/null
+++ b/clang/test/Modules/cxx20-import-paths.cpp
@@ -0,0 +1,80 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/one %t/two
+// RUN: split-file %s %t
+
+// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-01 -emit-header-unit \
+// RUN: -xc++-user-header %t/hu-01.h -o %t/one/hu-01.pcm
+// RUN: cp %t/one/hu-01.pcm %t/two/hu-01.pcm
+
+// A header unit loaded through a named mapping does not serialize its PCM path.
+// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-02 -emit-header-unit \
+// RUN: -xc++-user-header %t/hu-02.h -Wno-experimental-header-units \
+// RUN: -fmodule-map-file=%t/hu-01.map \
+// RUN: -fmodule-file=hu-01=%t/one/hu-01.pcm -o %t/hu-02-mapped.pcm
+// RUN: cp %t/hu-02-mapped.pcm %t/hu-02-mapped-from-one.pcm
+// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-02 -emit-header-unit \
+// RUN: -xc++-user-header %t/hu-02.h -Wno-experimental-header-units \
+// RUN: -fmodule-map-file=%t/hu-01.map \
+// RUN: -fmodule-file=hu-01=%t/two/hu-01.pcm -o %t/hu-02-mapped.pcm
+// RUN: diff %t/hu-02-mapped-from-one.pcm %t/hu-02-mapped.pcm
+
+// The dependency is loaded through its named mapping.
+// RUN: %clang_cc1 -std=c++20 -module-file-info %t/hu-02-mapped.pcm \
+// RUN: -fmodule-file=hu-01=%t/two/hu-01.pcm \
+// RUN: | FileCheck %s --check-prefix=HU-MAPPED
+// HU-MAPPED: Imports module 'hu-01':{{ *}}{{$}}
+
+// A pathless dependency requires a matching -fmodule-file=<name>=<pcm>.
+// RUN: not %clang_cc1 -std=c++20 -module-file-info %t/hu-02-mapped.pcm 2>&1 \
+// RUN: | FileCheck %s --check-prefix=MISSING
+// MISSING: error: failed to find module file for module 'hu-01'
+
+// A named mapping must reference a PCM with the expected module name.
+// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-other -emit-header-unit \
+// RUN: -xc++-user-header %t/hu-01.h -o %t/hu-other.pcm
+// RUN: not %clang_cc1 -std=c++20 -module-file-info %t/hu-02-mapped.pcm \
+// RUN: -fmodule-file=hu-01=%t/hu-other.pcm 2>&1 \
+// RUN: | FileCheck %s --check-prefix=MISMATCH
+// MISMATCH: error: failed to find module file for module 'hu-01'
+
+// An eagerly loaded header unit retains its PCM path even if a named mapping
+// also exists.
+// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-02 -emit-header-unit \
+// RUN: -xc++-user-header %t/hu-02.h -Wno-experimental-header-units \
+// RUN: -fmodule-map-file=%t/hu-01.map -fmodule-file=%t/one/hu-01.pcm \
+// RUN: -fmodule-file=hu-01=%t/one/hu-01.pcm -o %t/hu-02-eager.pcm
+// RUN: %clang_cc1 -std=c++20 -module-file-info %t/hu-02-eager.pcm \
+// RUN: -fmodule-file=hu-01=%t/one/hu-01.pcm \
+// RUN: | FileCheck %s --check-prefix=HU-EAGER -DPREFIX=%t
+// HU-EAGER: Imports module 'hu-01': [[PREFIX]]{{/|\\}}one{{/|\\}}hu-01.pcm
+
+// The PCM paths of C++20 named module dependencies are not serialized.
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/a.cppm \
+// RUN: -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/foo.cppm \
+// RUN: -fmodule-file=A=%t/A.pcm \
+// RUN: -o %t/Foo.pcm
+// RUN: %clang_cc1 -std=c++20 -module-file-info %t/Foo.pcm \
+// RUN: -fmodule-file=A=%t/A.pcm \
+// RUN: | FileCheck %s --check-prefix=NAMED-MODULE
+// NAMED-MODULE: Module name: Foo
+// NAMED-MODULE-NEXT: Imports module 'A':{{ *}}{{$}}
+
+//--- hu-01.h
+inline void f() {}
+
+//--- hu-02.h
+import "hu-01.h";
+
+//--- hu-01.map
+module "hu-01" {
+ header "hu-01.h"
+ export *
+}
+
+//--- a.cppm
+export module A;
+
+//--- foo.cppm
+export module Foo;
+import A;
More information about the cfe-commits
mailing list