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 <[email protected]> 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; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
