[clang] [clang][modules] Remove `ModuleFile::File` (PR #185995)

Jan Svoboda via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 24 08:45:43 PDT 2026


https://github.com/jansvoboda11 updated https://github.com/llvm/llvm-project/pull/185995

>From f92608f3d87dd35bcc8610fe3a5af985c1728d3c Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Mon, 9 Mar 2026 20:52:07 -0700
Subject: [PATCH 1/7] [clang][modules] Remove `ModuleFile::File`

---
 clang/include/clang/Basic/Module.h            |  6 +--
 clang/include/clang/Frontend/ASTUnit.h        |  2 +-
 clang/include/clang/Serialization/ASTReader.h |  4 +-
 clang/include/clang/Serialization/ASTWriter.h |  2 +-
 .../include/clang/Serialization/ModuleFile.h  | 13 ++---
 .../clang/Serialization/ModuleManager.h       |  8 ---
 .../DependencyScannerImpl.cpp                 |  9 ++--
 clang/lib/Frontend/ASTUnit.cpp                |  4 +-
 clang/lib/Frontend/DependencyFile.cpp         |  2 +-
 clang/lib/Frontend/FrontendAction.cpp         | 18 ++++++-
 .../lib/Frontend/Rewrite/FrontendActions.cpp  | 15 +++---
 clang/lib/Serialization/ASTReader.cpp         | 14 ++---
 clang/lib/Serialization/ASTWriter.cpp         | 14 ++---
 clang/lib/Serialization/GlobalModuleIndex.cpp |  3 +-
 clang/lib/Serialization/ModuleManager.cpp     | 52 ++++++-------------
 .../prebuilt-modules-in-stable-dirs.c         |  1 +
 clang/tools/libclang/CXIndexDataConsumer.cpp  |  5 +-
 clang/tools/libclang/CXIndexDataConsumer.h    |  2 +-
 clang/tools/libclang/Indexing.cpp             |  9 ++--
 19 files changed, 82 insertions(+), 101 deletions(-)

diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h
index 016cc2ce684b8..cb996430076e2 100644
--- a/clang/include/clang/Basic/Module.h
+++ b/clang/include/clang/Basic/Module.h
@@ -54,10 +54,6 @@ class TargetInfo;
 /// Describes the name of a module.
 using ModuleId = SmallVector<std::pair<std::string, SourceLocation>, 2>;
 
-namespace serialization {
-class ModuleManager;
-} // namespace serialization
-
 /// Deduplication key for a loaded module file in \c ModuleManager.
 ///
 /// For implicitly-built modules, this is the \c DirectoryEntry of the module
@@ -77,7 +73,7 @@ class ModuleFileKey {
   /// for other kinds of module files.
   std::string ImplicitModulePathSuffix;
 
-  friend class serialization::ModuleManager;
+  friend class ASTReader;
   friend class ModuleFileName;
   friend llvm::DenseMapInfo<ModuleFileKey>;
 
diff --git a/clang/include/clang/Frontend/ASTUnit.h b/clang/include/clang/Frontend/ASTUnit.h
index 7f307d1670dc6..b187494e449f2 100644
--- a/clang/include/clang/Frontend/ASTUnit.h
+++ b/clang/include/clang/Frontend/ASTUnit.h
@@ -679,7 +679,7 @@ class ASTUnit {
   bool visitLocalTopLevelDecls(void *context, DeclVisitorFn Fn);
 
   /// Get the PCH file if one was included.
-  OptionalFileEntryRef getPCHFile();
+  std::optional<StringRef> getPCHFile();
 
   /// Returns true if the ASTUnit was constructed from a serialized
   /// module file.
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index e9706d0ea2f2b..d6f75e5973c45 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -223,7 +223,7 @@ class ASTReaderListener {
   }
 
   /// This is called for each AST file loaded.
-  virtual void visitModuleFile(StringRef Filename,
+  virtual void visitModuleFile(ModuleFileName Filename,
                                serialization::ModuleKind Kind) {}
 
   /// Returns true if this \c ASTReaderListener wants to receive the
@@ -313,7 +313,7 @@ class ChainedASTReaderListener : public ASTReaderListener {
   void ReadCounter(const serialization::ModuleFile &M, uint32_t Value) override;
   bool needsInputFileVisitation() override;
   bool needsSystemInputFileVisitation() override;
-  void visitModuleFile(StringRef Filename,
+  void visitModuleFile(ModuleFileName Filename,
                        serialization::ModuleKind Kind) override;
   bool visitInputFile(StringRef Filename, bool isSystem,
                       bool isOverridden, bool isExplicitModule) override;
diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h
index 0f3993ad01693..be2fbdf1ade83 100644
--- a/clang/include/clang/Serialization/ASTWriter.h
+++ b/clang/include/clang/Serialization/ASTWriter.h
@@ -702,7 +702,7 @@ class ASTWriter : public ASTDeserializationListener,
   /// Get a timestamp for output into the AST file. The actual timestamp
   /// of the specified file may be ignored if we have been instructed to not
   /// include timestamps in the output file.
-  time_t getTimestampForOutput(const FileEntry *E) const;
+  time_t getTimestampForOutput(time_t ModTime) const;
 
   /// Write a precompiled header or a module with the AST produced by the
   /// \c Sema object, or a dependency scanner module with the preprocessor state
diff --git a/clang/include/clang/Serialization/ModuleFile.h b/clang/include/clang/Serialization/ModuleFile.h
index e761cadfcd86f..303bd65a8aad0 100644
--- a/clang/include/clang/Serialization/ModuleFile.h
+++ b/clang/include/clang/Serialization/ModuleFile.h
@@ -144,10 +144,8 @@ enum class InputFilesValidation {
 /// other modules.
 class ModuleFile {
 public:
-  ModuleFile(ModuleKind Kind, ModuleFileKey FileKey, FileEntryRef File,
-             unsigned Generation)
-      : Kind(Kind), FileKey(std::move(FileKey)), File(File),
-        Generation(Generation) {}
+  ModuleFile(ModuleKind Kind, ModuleFileKey FileKey, unsigned Generation)
+      : Kind(Kind), FileKey(std::move(FileKey)), Generation(Generation) {}
   ~ModuleFile();
 
   // === General information ===
@@ -201,8 +199,11 @@ class ModuleFile {
   /// Whether the top-level module has been read from the AST file.
   bool DidReadTopLevelSubmodule = false;
 
-  /// The file entry for the module file.
-  FileEntryRef File;
+  /// Size of the module file.
+  off_t Size = 0;
+
+  /// Modification of the module file.
+  time_t ModTime = 0;
 
   /// The signature of the module file, which may be used instead of the size
   /// and modification time to identify this particular file.
diff --git a/clang/include/clang/Serialization/ModuleManager.h b/clang/include/clang/Serialization/ModuleManager.h
index 162856f2f14c0..1ef9aeee7e1fd 100644
--- a/clang/include/clang/Serialization/ModuleManager.h
+++ b/clang/include/clang/Serialization/ModuleManager.h
@@ -172,17 +172,9 @@ class ModuleManager {
   /// Returns the module associated with the given index
   ModuleFile &operator[](unsigned Index) const { return *Chain[Index]; }
 
-  /// Returns the module associated with the given file name.
-  /// Soon to be removed.
-  ModuleFile *lookupByFileName(StringRef FileName) const;
-
   /// Returns the module associated with the given module name.
   ModuleFile *lookupByModuleName(StringRef ModName) const;
 
-  /// Returns the module associated with the given module file.
-  /// Soon to be removed.
-  ModuleFile *lookup(const FileEntry *File) const;
-
   /// Returns the module associated with the given module file name.
   ModuleFile *lookupByFileName(ModuleFileName FileName) const;
 
diff --git a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
index 20284c0d9165a..f882713a8b76d 100644
--- a/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
+++ b/clang/lib/DependencyScanning/DependencyScannerImpl.cpp
@@ -135,7 +135,7 @@ class PrebuiltModuleListener : public ASTReaderListener {
   }
 
   /// Update which module that is being actively traversed.
-  void visitModuleFile(StringRef Filename,
+  void visitModuleFile(ModuleFileName Filename,
                        serialization::ModuleKind Kind) override {
     // If the CurrentFile is not
     // considered stable, update any of it's transitive dependents.
@@ -144,7 +144,7 @@ class PrebuiltModuleListener : public ASTReaderListener {
         !PrebuiltEntryIt->second.isInStableDir())
       PrebuiltEntryIt->second.updateDependentsNotInStableDirs(
           PrebuiltModulesASTMap);
-    CurrentFile = Filename;
+    CurrentFile = Filename.str();
   }
 
   /// Check the header search options for a given module when considering
@@ -206,7 +206,7 @@ static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
                                   CI.getHeaderSearchOpts(), CI.getLangOpts(),
                                   Diags, StableDirs);
 
-  Listener.visitModuleFile(PrebuiltModuleFilename,
+  Listener.visitModuleFile(ModuleFileName::makeExplicit(PrebuiltModuleFilename),
                            serialization::MK_ExplicitModule);
   if (ASTReader::readASTFileControlBlock(
           PrebuiltModuleFilename, CI.getFileManager(), CI.getModuleCache(),
@@ -216,7 +216,8 @@ static bool visitPrebuiltModule(StringRef PrebuiltModuleFilename,
     return true;
 
   while (!Worklist.empty()) {
-    Listener.visitModuleFile(Worklist.back(), serialization::MK_ExplicitModule);
+    Listener.visitModuleFile(ModuleFileName::makeExplicit(Worklist.back()),
+                             serialization::MK_ExplicitModule);
     if (ASTReader::readASTFileControlBlock(
             Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
             CI.getPCHContainerReader(),
diff --git a/clang/lib/Frontend/ASTUnit.cpp b/clang/lib/Frontend/ASTUnit.cpp
index 1e10178285bbd..05ae1f348f920 100644
--- a/clang/lib/Frontend/ASTUnit.cpp
+++ b/clang/lib/Frontend/ASTUnit.cpp
@@ -2443,7 +2443,7 @@ bool ASTUnit::visitLocalTopLevelDecls(void *context, DeclVisitorFn Fn) {
   return true;
 }
 
-OptionalFileEntryRef ASTUnit::getPCHFile() {
+std::optional<StringRef> ASTUnit::getPCHFile() {
   if (!Reader)
     return std::nullopt;
 
@@ -2466,7 +2466,7 @@ OptionalFileEntryRef ASTUnit::getPCHFile() {
     return true;
   });
   if (Mod)
-    return Mod->File;
+    return Mod->FileName;
 
   return std::nullopt;
 }
diff --git a/clang/lib/Frontend/DependencyFile.cpp b/clang/lib/Frontend/DependencyFile.cpp
index 25584b4900228..64629abcaeb52 100644
--- a/clang/lib/Frontend/DependencyFile.cpp
+++ b/clang/lib/Frontend/DependencyFile.cpp
@@ -154,7 +154,7 @@ struct DepCollectorASTListener : public ASTReaderListener {
   bool needsSystemInputFileVisitation() override {
     return DepCollector.needSystemDependencies();
   }
-  void visitModuleFile(StringRef Filename,
+  void visitModuleFile(ModuleFileName Filename,
                        serialization::ModuleKind Kind) override {
     DepCollector.maybeAddDependency(Filename, /*FromModule*/ true,
                                     /*IsSystem*/ false, /*IsModuleFile*/ true,
diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
index 81788d17acc4d..c3d1d802dfe04 100644
--- a/clang/lib/Frontend/FrontendAction.cpp
+++ b/clang/lib/Frontend/FrontendAction.cpp
@@ -850,6 +850,11 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
   if (!BeginInvocation(CI))
     return false;
 
+  // The list of module files the input AST file depends on. This is separate
+  // from FrontendOptions::ModuleFiles, because those only represent explicit
+  // modules, while this is capable of representing implicit ones too.
+  SmallVector<ModuleFileName> ModuleFiles;
+
   // If we're replaying the build of an AST file, import it and set up
   // the initial state from its build.
   if (ReplayASTFile) {
@@ -892,7 +897,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
 
       for (serialization::ModuleFile &MF : MM)
         if (&MF != &PrimaryModule)
-          CI.getFrontendOpts().ModuleFiles.emplace_back(MF.FileName.str());
+          ModuleFiles.emplace_back(MF.FileName);
 
       ASTReader->visitTopLevelModuleMaps(PrimaryModule, [&](FileEntryRef FE) {
         CI.getFrontendOpts().ModuleMapFiles.push_back(
@@ -1296,6 +1301,17 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
           diag::warn_eagerly_load_for_standard_cplusplus_modules);
   }
 
+  // If we were asked to load any module files by the ASTUnit, do so now.
+  for (const auto &ModuleFile : ModuleFiles) {
+    serialization::ModuleFile *Loaded = nullptr;
+    if (!CI.loadModuleFile(ModuleFile, Loaded))
+      return false;
+
+    if (Loaded && Loaded->StandardCXXModule)
+      CI.getDiagnostics().Report(
+          diag::warn_eagerly_load_for_standard_cplusplus_modules);
+  }
+
   // If there is a layout overrides file, attach an external AST source that
   // provides the layouts from that file.
   if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() &&
diff --git a/clang/lib/Frontend/Rewrite/FrontendActions.cpp b/clang/lib/Frontend/Rewrite/FrontendActions.cpp
index ef6f9ccf87848..1e12d3a6ea3df 100644
--- a/clang/lib/Frontend/Rewrite/FrontendActions.cpp
+++ b/clang/lib/Frontend/Rewrite/FrontendActions.cpp
@@ -205,25 +205,22 @@ class RewriteIncludesAction::RewriteImportsListener : public ASTReaderListener {
   CompilerInstance &CI;
   std::weak_ptr<raw_ostream> Out;
 
-  llvm::DenseSet<const FileEntry*> Rewritten;
+  llvm::DenseSet<const serialization::ModuleFile *> Rewritten;
 
 public:
   RewriteImportsListener(CompilerInstance &CI, std::shared_ptr<raw_ostream> Out)
       : CI(CI), Out(Out) {}
 
-  void visitModuleFile(StringRef Filename,
+  void visitModuleFile(ModuleFileName Filename,
                        serialization::ModuleKind Kind) override {
-    auto File = CI.getFileManager().getOptionalFileRef(Filename);
-    assert(File && "missing file for loaded module?");
+    serialization::ModuleFile *MF =
+        CI.getASTReader()->getModuleManager().lookupByFileName(Filename);
+    assert(MF && "missing module file for loaded module?");
 
     // Only rewrite each module file once.
-    if (!Rewritten.insert(*File).second)
+    if (!Rewritten.insert(MF).second)
       return;
 
-    serialization::ModuleFile *MF =
-        CI.getASTReader()->getModuleManager().lookup(*File);
-    assert(MF && "missing module file for loaded module?");
-
     // Not interested in PCH / preambles.
     if (!MF->isModule())
       return;
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 03b1b02859b81..9274d4cc6f0fa 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -240,7 +240,7 @@ bool ChainedASTReaderListener::needsSystemInputFileVisitation() {
   Second->needsSystemInputFileVisitation();
 }
 
-void ChainedASTReaderListener::visitModuleFile(StringRef Filename,
+void ChainedASTReaderListener::visitModuleFile(ModuleFileName Filename,
                                                ModuleKind Kind) {
   First->visitModuleFile(Filename, Kind);
   Second->visitModuleFile(Filename, Kind);
@@ -3193,8 +3193,7 @@ ASTReader::getModuleForRelocationChecks(ModuleFile &F, bool DirectoryCheck) {
   if (HSOpts.ModulesValidateOncePerBuildSession && IsImplicitModule) {
     if (F.InputFilesValidationTimestamp >= HSOpts.BuildSessionTimestamp)
       return {std::nullopt, IgnoreError};
-    if (static_cast<uint64_t>(F.File.getModificationTime()) >=
-        HSOpts.BuildSessionTimestamp)
+    if (static_cast<uint64_t>(F.ModTime) >= HSOpts.BuildSessionTimestamp)
       return {std::nullopt, IgnoreError};
   }
 
@@ -4593,10 +4592,11 @@ void ASTReader::ReadModuleOffsetMap(ModuleFile &F) const {
     uint16_t Len = endian::readNext<uint16_t, llvm::endianness::little>(Data);
     StringRef Name = StringRef((const char*)Data, Len);
     Data += Len;
-    ModuleFile *OM = (Kind == MK_PrebuiltModule || Kind == MK_ExplicitModule ||
-                              Kind == MK_ImplicitModule
-                          ? ModuleMgr.lookupByModuleName(Name)
-                          : ModuleMgr.lookupByFileName(Name));
+    ModuleFile *OM =
+        (Kind == MK_PrebuiltModule || Kind == MK_ExplicitModule ||
+                 Kind == MK_ImplicitModule
+             ? ModuleMgr.lookupByModuleName(Name)
+             : ModuleMgr.lookupByFileName(ModuleFileName::makeExplicit(Name)));
     if (!OM) {
       std::string Msg = "refers to unknown module, cannot find ";
       Msg.append(std::string(Name));
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index e3db39a1acb74..2b0808ad5ad5e 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -1598,8 +1598,8 @@ void ASTWriter::WriteControlBlock(Preprocessor &PP, StringRef isysroot) {
       } else {
         // If we have calculated signature, there is no need to store
         // the size or timestamp.
-        Record.push_back(M.Signature ? 0 : M.File.getSize());
-        Record.push_back(M.Signature ? 0 : getTimestampForOutput(M.File));
+        Record.push_back(M.Signature ? 0 : M.Size);
+        Record.push_back(M.Signature ? 0 : getTimestampForOutput(M.ModTime));
 
         llvm::append_range(Blob, M.Signature);
 
@@ -1963,7 +1963,7 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr) {
           INPUT_FILE,
           InputFileOffsets.size(),
           (uint64_t)Entry.File.getSize(),
-          (uint64_t)getTimestampForOutput(Entry.File),
+          (uint64_t)getTimestampForOutput(Entry.File.getModificationTime()),
           Entry.BufferOverridden,
           Entry.IsTransient,
           Entry.IsTopLevel,
@@ -2280,8 +2280,8 @@ void ASTWriter::WriteHeaderSearch(const HeaderSearch &HS) {
     bool Included = HFI->IsLocallyIncluded || PP->alreadyIncluded(*File);
 
     HeaderFileInfoTrait::key_type Key = {
-      Filename, File->getSize(), getTimestampForOutput(*File)
-    };
+        Filename, File->getSize(),
+        getTimestampForOutput(File->getModificationTime())};
     HeaderFileInfoTrait::data_type Data = {
       *HFI, Included, HS.getModuleMap().findResolvedModulesForHeader(*File), {}
     };
@@ -5488,8 +5488,8 @@ const LangOptions &ASTWriter::getLangOpts() const {
   return PP->getLangOpts();
 }
 
-time_t ASTWriter::getTimestampForOutput(const FileEntry *E) const {
-  return IncludeTimestamps ? E->getModificationTime() : 0;
+time_t ASTWriter::getTimestampForOutput(time_t ModTime) const {
+  return IncludeTimestamps ? ModTime : 0;
 }
 
 ASTFileSignature
diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp
index 8ab8717776c3d..3f6b8e68e38f3 100644
--- a/clang/lib/Serialization/GlobalModuleIndex.cpp
+++ b/clang/lib/Serialization/GlobalModuleIndex.cpp
@@ -342,8 +342,7 @@ bool GlobalModuleIndex::loadedModuleFile(ModuleFile *File) {
   //  If the size and modification time match what we expected, record this
   // module file.
   bool Failed = true;
-  if (File->File.getSize() == Info.Size &&
-      File->File.getModificationTime() == Info.ModTime) {
+  if (File->Size == Info.Size && File->ModTime == Info.ModTime) {
     Info.File = File;
     ModulesByFile[File] = Known->second;
 
diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp
index abb5b7955c8fa..6768028cd3ae3 100644
--- a/clang/lib/Serialization/ModuleManager.cpp
+++ b/clang/lib/Serialization/ModuleManager.cpp
@@ -40,15 +40,6 @@
 using namespace clang;
 using namespace serialization;
 
-ModuleFile *ModuleManager::lookupByFileName(StringRef Name) const {
-  auto Entry = FileMgr.getOptionalFileRef(Name, /*OpenFile=*/false,
-                                          /*CacheFailure=*/false);
-  if (Entry)
-    return lookup(*Entry);
-
-  return nullptr;
-}
-
 ModuleFile *ModuleManager::lookupByModuleName(StringRef Name) const {
   if (const Module *Mod = HeaderSearchInfo.getModuleMap().findModule(Name))
     if (const ModuleFileName *FileName = Mod->getASTFileName())
@@ -57,10 +48,6 @@ ModuleFile *ModuleManager::lookupByModuleName(StringRef Name) const {
   return nullptr;
 }
 
-ModuleFile *ModuleManager::lookup(const FileEntry *File) const {
-  return lookup(ModuleFileKey(File));
-}
-
 ModuleFile *ModuleManager::lookupByFileName(ModuleFileName Name) const {
   std::optional<ModuleFileKey> Key = Name.makeKey(FileMgr);
   return Key ? lookup(*Key) : nullptr;
@@ -79,16 +66,14 @@ ModuleManager::lookupBuffer(StringRef Name) {
   return std::move(InMemoryBuffers[*Entry]);
 }
 
-static bool checkModuleFile(const FileEntry *File, off_t ExpectedSize,
+static bool checkModuleFile(off_t Size, time_t ModTime, off_t ExpectedSize,
                             time_t ExpectedModTime, std::string &ErrorStr) {
-  assert(File && "Checking expectations of a non-existent module file");
-
-  if (ExpectedSize && ExpectedSize != File->getSize()) {
+  if (ExpectedSize && ExpectedSize != Size) {
     ErrorStr = "module file has a different size than expected";
     return true;
   }
 
-  if (ExpectedModTime && ExpectedModTime != File->getModificationTime()) {
+  if (ExpectedModTime && ExpectedModTime != ModTime) {
     ErrorStr = "module file has a different modification time than expected";
     return true;
   }
@@ -153,8 +138,8 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
   // Check whether we already loaded this module, before
   if (ModuleFile *ModuleEntry = lookup(*FileKey)) {
     // Check file properties.
-    if (checkModuleFile(ModuleEntry->File, ExpectedSize, ExpectedModTime,
-                        ErrorStr))
+    if (checkModuleFile(ModuleEntry->Size, ModuleEntry->ModTime, ExpectedSize,
+                        ExpectedModTime, ErrorStr))
       return OutOfDate;
 
     // Check the stored signature.
@@ -167,7 +152,8 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
   }
 
   // Load the contents of the module
-  OptionalFileEntryRef Entry;
+  off_t Size = ExpectedSize;
+  time_t ModTime = ExpectedModTime;
   llvm::MemoryBuffer *ModuleBuffer = nullptr;
   std::unique_ptr<llvm::MemoryBuffer> NewFileBuffer = nullptr;
   if (std::unique_ptr<llvm::MemoryBuffer> Buffer = lookupBuffer(FileName)) {
@@ -184,7 +170,7 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
     // import it earlier.
     return OutOfDate;
   } else {
-    Entry =
+    OptionalFileEntryRef Entry =
         expectedToOptional(FileName == StringRef("-")
                                ? FileMgr.getSTDIN()
                                : FileMgr.getFileRef(FileName, /*OpenFile=*/true,
@@ -198,7 +184,8 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
     // size/mtime expectations even when pulling the module file out of the
     // in-memory module cache or the provided in-memory buffers.
     // Check file properties.
-    if (checkModuleFile(*Entry, ExpectedSize, ExpectedModTime, ErrorStr))
+    if (checkModuleFile(Entry->getSize(), Entry->getModificationTime(),
+                        ExpectedSize, ExpectedModTime, ErrorStr))
       return OutOfDate;
 
     // Get a buffer of the file and close the file descriptor when done.
@@ -216,24 +203,20 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
       return Missing;
     }
 
+    Size = Entry->getSize();
+    ModTime = Entry->getModificationTime();
     NewFileBuffer = std::move(*Buf);
     ModuleBuffer = NewFileBuffer.get();
   }
 
-  if (!Entry) {
-    // Unless we loaded the buffer from a freshly open file (else branch above),
-    // we don't have any FileEntry for this ModuleFile. Make one up.
-    // FIXME: Make it so that ModuleFile is not tied to a FileEntry.
-    Entry = FileMgr.getVirtualFileRef(FileName, ExpectedSize, ExpectedModTime);
-  }
-
   // Allocate a new module.
-  auto NewModule =
-      std::make_unique<ModuleFile>(Type, *FileKey, *Entry, Generation);
+  auto NewModule = std::make_unique<ModuleFile>(Type, *FileKey, Generation);
   NewModule->Index = Chain.size();
   NewModule->FileName = FileName;
   NewModule->ImportLoc = ImportLoc;
   NewModule->InputFilesValidationTimestamp = InputFilesValidationTimestamp;
+  NewModule->Size = Size;
+  NewModule->ModTime = ModTime;
   NewModule->Buffer = ModuleBuffer;
   // Initialize the stream.
   NewModule->Data = PCHContainerRdr.ExtractPCH(*NewModule->Buffer);
@@ -251,11 +234,6 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
   // We're keeping this module. Store it in the map.
   Module = Modules[*FileKey] = NewModule.get();
 
-  // Support clients that still rely on being able to look up ModuleFile with
-  // normal FileEntry.
-  // TODO: Remove this.
-  Modules[ModuleFileKey(*Entry)] = Module;
-
   updateModuleImports(*NewModule, ImportedBy, ImportLoc);
 
   if (!NewModule->isModule())
diff --git a/clang/test/ClangScanDeps/prebuilt-modules-in-stable-dirs.c b/clang/test/ClangScanDeps/prebuilt-modules-in-stable-dirs.c
index 39b2863d966c3..29fc9dda2953c 100644
--- a/clang/test/ClangScanDeps/prebuilt-modules-in-stable-dirs.c
+++ b/clang/test/ClangScanDeps/prebuilt-modules-in-stable-dirs.c
@@ -15,6 +15,7 @@
 // RUN: sed -e "s|DIR|%/t|g" %t/compile-pch.json.in > %t/compile-pch.json
 // RUN: clang-scan-deps -compilation-database %t/compile-pch.json \
 // RUN:   -j 1 -format experimental-full > %t/deps_pch.db
+// FIXME: We can't just build the PCH implicitly and use it in the scan.
 // RUN: %clang -x c-header -c %t/prebuild.h -isysroot %t/MacOSX.sdk \
 // RUN:   -I%t/BuildDir -ivfsoverlay %t/overlay.json \
 // RUN:   -I %t/MacOSX.sdk/usr/include -fmodules -fmodules-cache-path=%t/module-cache \
diff --git a/clang/tools/libclang/CXIndexDataConsumer.cpp b/clang/tools/libclang/CXIndexDataConsumer.cpp
index 265d5876ee7a6..8babcccf38c51 100644
--- a/clang/tools/libclang/CXIndexDataConsumer.cpp
+++ b/clang/tools/libclang/CXIndexDataConsumer.cpp
@@ -517,10 +517,13 @@ void CXIndexDataConsumer::importedModule(const ImportDecl *ImportD) {
   (void)astFile;
 }
 
-void CXIndexDataConsumer::importedPCH(FileEntryRef File) {
+void CXIndexDataConsumer::importedPCH(StringRef FileName) {
   if (!CB.importedASTFile)
     return;
 
+  FileManager &FileMgr = cxtu::getASTUnit(CXTU)->getFileManager();
+  OptionalFileEntryRef File = FileMgr.getOptionalFileRef(FileName);
+
   CXIdxImportedASTFileInfo Info = {
                                     cxfile::makeCXFile(File),
                                     /*module=*/nullptr,
diff --git a/clang/tools/libclang/CXIndexDataConsumer.h b/clang/tools/libclang/CXIndexDataConsumer.h
index b207db7cde6d7..3608f76a6fb8f 100644
--- a/clang/tools/libclang/CXIndexDataConsumer.h
+++ b/clang/tools/libclang/CXIndexDataConsumer.h
@@ -367,7 +367,7 @@ class CXIndexDataConsumer : public index::IndexDataConsumer {
                       bool isModuleImport);
 
   void importedModule(const ImportDecl *ImportD);
-  void importedPCH(FileEntryRef File);
+  void importedPCH(StringRef FileName);
 
   void startedTranslationUnit();
 
diff --git a/clang/tools/libclang/Indexing.cpp b/clang/tools/libclang/Indexing.cpp
index 75323d70afcfe..4e57b63490112 100644
--- a/clang/tools/libclang/Indexing.cpp
+++ b/clang/tools/libclang/Indexing.cpp
@@ -351,11 +351,8 @@ class IndexingFrontendAction : public ASTFrontendAction {
                                                  StringRef InFile) override {
     PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
 
-    if (!PPOpts.ImplicitPCHInclude.empty()) {
-      if (auto File =
-              CI.getFileManager().getOptionalFileRef(PPOpts.ImplicitPCHInclude))
-        DataConsumer->importedPCH(*File);
-    }
+    if (!PPOpts.ImplicitPCHInclude.empty())
+      DataConsumer->importedPCH(PPOpts.ImplicitPCHInclude);
 
     DataConsumer->setASTContext(CI.getASTContextPtr());
     Preprocessor &PP = CI.getPreprocessor();
@@ -695,7 +692,7 @@ static CXErrorCode clang_indexTranslationUnit_Impl(
 
   ASTUnit::ConcurrencyCheck Check(*Unit);
 
-  if (OptionalFileEntryRef PCHFile = Unit->getPCHFile())
+  if (std::optional<StringRef> PCHFile = Unit->getPCHFile())
     DataConsumer.importedPCH(*PCHFile);
 
   FileManager &FileMgr = Unit->getFileManager();

>From 8abf3cc48d6ddff6e1182168920765b3ea725f46 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Thu, 19 Mar 2026 16:02:40 -0700
Subject: [PATCH 2/7] End an unnecessary friendship

---
 clang/include/clang/Basic/Module.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h
index cb996430076e2..70668860dadc2 100644
--- a/clang/include/clang/Basic/Module.h
+++ b/clang/include/clang/Basic/Module.h
@@ -73,7 +73,6 @@ class ModuleFileKey {
   /// for other kinds of module files.
   std::string ImplicitModulePathSuffix;
 
-  friend class ASTReader;
   friend class ModuleFileName;
   friend llvm::DenseMapInfo<ModuleFileKey>;
 

>From 82f3ff21b646139a62906ec79d1d55d336238967 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 20 Mar 2026 14:48:20 -0700
Subject: [PATCH 3/7] Fix "clang/test/ClangScanDeps/modules-pch-common-stale.c"

---
 clang/lib/Serialization/ModuleManager.cpp | 30 +++++++++++++++++------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp
index 6768028cd3ae3..ed7b6cf67674e 100644
--- a/clang/lib/Serialization/ModuleManager.cpp
+++ b/clang/lib/Serialization/ModuleManager.cpp
@@ -164,6 +164,24 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
                  getModuleCache().getInMemoryModuleCache().lookupPCM(
                      FileName)) {
     ModuleBuffer = Buffer;
+    if (!FileName.getImplicitModuleSuffixLength()) {
+      // Explicitly-built PCM files maintain consistency via mtime/size
+      // expectations on their imports. Even if we've previously successfully
+      // loaded a PCM file and stored it in the in-memory module cache, that
+      // does not mean its mtime/size matches current importer's expectations.
+      // Get that information so that it can be checked below.
+      // FIXME: Even though this FileManager access is likely already cached, we
+      // should store this directly in the in-memory module cache.
+      OptionalFileEntryRef Entry =
+          FileMgr.getOptionalFileRef(FileName, /*OpenFile=*/true,
+                                     /*CacheFailure=*/false);
+      if (!Entry) {
+        ErrorStr = "module file not found";
+        return Missing;
+      }
+      ModTime = Entry->getModificationTime();
+      Size = Entry->getSize();
+    }
   } else if (getModuleCache().getInMemoryModuleCache().shouldBuildPCM(
                  FileName)) {
     // Report that the module is out of date, since we tried (and failed) to
@@ -180,14 +198,6 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
       return Missing;
     }
 
-    // FIXME: Consider moving this after this else branch so that we check
-    // size/mtime expectations even when pulling the module file out of the
-    // in-memory module cache or the provided in-memory buffers.
-    // Check file properties.
-    if (checkModuleFile(Entry->getSize(), Entry->getModificationTime(),
-                        ExpectedSize, ExpectedModTime, ErrorStr))
-      return OutOfDate;
-
     // Get a buffer of the file and close the file descriptor when done.
     // The file is volatile because in a parallel build we expect multiple
     // compiler processes to use the same module file rebuilding it if needed.
@@ -221,6 +231,10 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
   // Initialize the stream.
   NewModule->Data = PCHContainerRdr.ExtractPCH(*NewModule->Buffer);
 
+  // Check file properties.
+  if (checkModuleFile(Size, ModTime, ExpectedSize, ExpectedModTime, ErrorStr))
+    return OutOfDate;
+
   // Read the signature eagerly now so that we can check it.  Avoid calling
   // ReadSignature unless there's something to check though.
   if (ExpectedSignature && checkSignature(ReadSignature(NewModule->Data),

>From 985cd156486a9576e95bc2f92daebe0bb9283d05 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 20 Mar 2026 14:48:38 -0700
Subject: [PATCH 4/7] Fix
 "clang/test/ClangScanDeps/prebuilt-modules-in-stable-dirs.c"

---
 .../prebuilt-modules-in-stable-dirs.c           | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/clang/test/ClangScanDeps/prebuilt-modules-in-stable-dirs.c b/clang/test/ClangScanDeps/prebuilt-modules-in-stable-dirs.c
index 29fc9dda2953c..54b4b23771ca3 100644
--- a/clang/test/ClangScanDeps/prebuilt-modules-in-stable-dirs.c
+++ b/clang/test/ClangScanDeps/prebuilt-modules-in-stable-dirs.c
@@ -15,11 +15,18 @@
 // RUN: sed -e "s|DIR|%/t|g" %t/compile-pch.json.in > %t/compile-pch.json
 // RUN: clang-scan-deps -compilation-database %t/compile-pch.json \
 // RUN:   -j 1 -format experimental-full > %t/deps_pch.db
-// FIXME: We can't just build the PCH implicitly and use it in the scan.
-// RUN: %clang -x c-header -c %t/prebuild.h -isysroot %t/MacOSX.sdk \
-// RUN:   -I%t/BuildDir -ivfsoverlay %t/overlay.json \
-// RUN:   -I %t/MacOSX.sdk/usr/include -fmodules -fmodules-cache-path=%t/module-cache \
-// RUN:   -fimplicit-module-maps -o %t/prebuild.pch
+
+// RUN: %deps-to-rsp %t/deps_pch.db --module-name=A > %t/A.rsp
+// RUN: %deps-to-rsp %t/deps_pch.db --module-name=B > %t/B.rsp
+// RUN: %deps-to-rsp %t/deps_pch.db --module-name=B_transitive > %t/B_transitive.rsp
+// RUN: %deps-to-rsp %t/deps_pch.db --module-name=C > %t/C.rsp
+// RUN: %deps-to-rsp %t/deps_pch.db --tu-index=0 > %t/pch.rsp
+// RUN: %clang @%t/A.rsp
+// RUN: %clang @%t/B.rsp
+// RUN: %clang @%t/B_transitive.rsp
+// RUN: %clang @%t/C.rsp
+// RUN: %clang @%t/pch.rsp
+
 // RUN: sed -e "s|DIR|%/t|g" %t/compile-commands.json.in > %t/compile-commands.json
 // RUN: clang-scan-deps -compilation-database %t/compile-commands.json \
 // RUN:   -j 1 -format experimental-full > %t/deps.db

>From 3d2f13064ec41f65fa01208bfd9992826c866ef2 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 20 Mar 2026 20:54:01 -0700
Subject: [PATCH 5/7] Write to module cache

---
 .../include/clang/Frontend/CompilerInstance.h | 10 +++--
 .../include/clang/Frontend/FrontendActions.h  | 12 ++++++
 .../include/clang/Serialization/ModuleCache.h |  7 +++-
 .../InProcessModuleCache.cpp                  |  6 +++
 clang/lib/Frontend/CompilerInstance.cpp       | 42 ++++++++++++++-----
 clang/lib/Frontend/FrontendActions.cpp        |  3 +-
 clang/lib/Serialization/ModuleCache.cpp       | 33 +++++++++++++++
 7 files changed, 96 insertions(+), 17 deletions(-)

diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h
index 0d684d5c7f9fe..01f83498d8c8e 100644
--- a/clang/include/clang/Frontend/CompilerInstance.h
+++ b/clang/include/clang/Frontend/CompilerInstance.h
@@ -937,12 +937,14 @@ class CompilerInstance : public ModuleLoader {
       std::optional<ThreadSafeCloneConfig> ThreadSafeConfig = std::nullopt);
 
   /// Compile a module file for the given module, using the options
-  /// provided by the importing compiler instance. Returns true if the module
-  /// was built without errors.
+  /// provided by the importing compiler instance. Returns the PCM file in
+  /// a buffer.
   // FIXME: This should be private, but it's called from static non-member
   // functions in the implementation file.
-  bool compileModule(SourceLocation ImportLoc, StringRef ModuleName,
-                     StringRef ModuleFileName, CompilerInstance &Instance);
+  std::unique_ptr<llvm::MemoryBuffer> compileModule(SourceLocation ImportLoc,
+                                                    StringRef ModuleName,
+                                                    StringRef ModuleFileName,
+                                                    CompilerInstance &Instance);
 
   ModuleLoadResult loadModule(SourceLocation ImportLoc, ModuleIdPath Path,
                               Module::NameVisibilityKind Visibility,
diff --git a/clang/include/clang/Frontend/FrontendActions.h b/clang/include/clang/Frontend/FrontendActions.h
index 87a9f0d4cb06c..3d4da567e18dd 100644
--- a/clang/include/clang/Frontend/FrontendActions.h
+++ b/clang/include/clang/Frontend/FrontendActions.h
@@ -114,6 +114,13 @@ class GeneratePCHAction : public ASTFrontendAction {
 };
 
 class GenerateModuleAction : public ASTFrontendAction {
+public:
+  explicit GenerateModuleAction(std::unique_ptr<raw_pwrite_stream> OS = nullptr)
+      : OS(std::move(OS)) {}
+
+private:
+  std::unique_ptr<raw_pwrite_stream> OS;
+
   virtual std::unique_ptr<raw_pwrite_stream>
   CreateOutputFile(CompilerInstance &CI, StringRef InFile) = 0;
 
@@ -145,6 +152,11 @@ class GenerateInterfaceStubsAction : public ASTFrontendAction {
 };
 
 class GenerateModuleFromModuleMapAction : public GenerateModuleAction {
+public:
+  explicit GenerateModuleFromModuleMapAction(
+      std::unique_ptr<raw_pwrite_stream> OS = nullptr)
+      : GenerateModuleAction(std::move(OS)) {}
+
 private:
   bool BeginSourceFileAction(CompilerInstance &CI) override;
 
diff --git a/clang/include/clang/Serialization/ModuleCache.h b/clang/include/clang/Serialization/ModuleCache.h
index c6795c5dc358a..febc642d205ab 100644
--- a/clang/include/clang/Serialization/ModuleCache.h
+++ b/clang/include/clang/Serialization/ModuleCache.h
@@ -14,6 +14,7 @@
 #include <ctime>
 
 namespace llvm {
+class MemoryBufferRef;
 class AdvisoryLock;
 } // namespace llvm
 
@@ -52,7 +53,9 @@ class ModuleCache {
   virtual InMemoryModuleCache &getInMemoryModuleCache() = 0;
   virtual const InMemoryModuleCache &getInMemoryModuleCache() const = 0;
 
-  // TODO: Virtualize writing/reading PCM files, etc.
+  virtual time_t write(StringRef Path, llvm::MemoryBufferRef Buffer) = 0;
+
+  // TODO: Virtualize reading PCM files, etc.
 
   virtual ~ModuleCache() = default;
 };
@@ -65,6 +68,8 @@ std::shared_ptr<ModuleCache> createCrossProcessModuleCache();
 
 /// Shared implementation of `ModuleCache::maybePrune()`.
 void maybePruneImpl(StringRef Path, time_t PruneInterval, time_t PruneAfter);
+
+time_t writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer);
 } // namespace clang
 
 #endif
diff --git a/clang/lib/DependencyScanning/InProcessModuleCache.cpp b/clang/lib/DependencyScanning/InProcessModuleCache.cpp
index cd7385c8f38c2..16d0893690c39 100644
--- a/clang/lib/DependencyScanning/InProcessModuleCache.cpp
+++ b/clang/lib/DependencyScanning/InProcessModuleCache.cpp
@@ -127,6 +127,12 @@ class InProcessModuleCache : public ModuleCache {
     maybePruneImpl(Path, PruneInterval, PruneAfter);
   }
 
+  time_t write(StringRef Path, llvm::MemoryBufferRef Buffer) override {
+    // FIXME: This could use an in-memory cache to avoid IO, and only write all
+    // PCMs at the end.
+    return writeImpl(Path, Buffer);
+  }
+
   InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
   const InMemoryModuleCache &getInMemoryModuleCache() const override {
     return InMemory;
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 33919bc1c4634..7647f80c2f96a 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1236,7 +1236,7 @@ class PrettyStackTraceBuildModule : public llvm::PrettyStackTraceEntry {
 };
 } // namespace
 
-bool CompilerInstance::compileModule(SourceLocation ImportLoc,
+std::unique_ptr<llvm::MemoryBuffer> CompilerInstance::compileModule(SourceLocation ImportLoc,
                                      StringRef ModuleName,
                                      StringRef ModuleFileName,
                                      CompilerInstance &Instance) {
@@ -1248,18 +1248,22 @@ bool CompilerInstance::compileModule(SourceLocation ImportLoc,
   if (getModuleCache().getInMemoryModuleCache().isPCMFinal(ModuleFileName)) {
     getDiagnostics().Report(ImportLoc, diag::err_module_rebuild_finalized)
         << ModuleName;
-    return false;
+    return nullptr;
   }
 
   getDiagnostics().Report(ImportLoc, diag::remark_module_build)
       << ModuleName << ModuleFileName;
 
+  SmallString<0> Buffer;
+
   // Execute the action to actually build the module in-place. Use a separate
   // thread so that we get a stack large enough.
   bool Crashed = !llvm::CrashRecoveryContext().RunSafelyOnNewStack(
       [&]() {
+        auto OS = std::make_unique<llvm::raw_svector_ostream>(Buffer);
+
         std::unique_ptr<FrontendAction> Action =
-            std::make_unique<GenerateModuleFromModuleMapAction>();
+            std::make_unique<GenerateModuleFromModuleMapAction>(std::move(OS));
 
         if (auto WrapGenModuleAction = Instance.getGenModuleActionWrapper())
           Action = WrapGenModuleAction(Instance.getFrontendOpts(),
@@ -1297,8 +1301,11 @@ bool CompilerInstance::compileModule(SourceLocation ImportLoc,
 
   // If \p AllowPCMWithCompilerErrors is set return 'success' even if errors
   // occurred.
-  return !Instance.getDiagnostics().hasErrorOccurred() ||
-         Instance.getFrontendOpts().AllowPCMWithCompilerErrors;
+  if (!Instance.getDiagnostics().hasErrorOccurred() ||
+      Instance.getFrontendOpts().AllowPCMWithCompilerErrors)
+    return llvm::MemoryBuffer::getMemBufferCopy(
+        Buffer, Instance.getFrontendOpts().OutputFile);
+  return nullptr;
 }
 
 static OptionalFileEntryRef getPublicModuleMap(FileEntryRef File,
@@ -1440,13 +1447,17 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
                               SourceLocation ImportLoc,
                               SourceLocation ModuleNameLoc, Module *Module,
                               ModuleFileName ModuleFileName) {
+  std::unique_ptr<llvm::MemoryBuffer> Buffer;
+
   {
     auto Instance = ImportingInstance.cloneForModuleCompile(
         ModuleNameLoc, Module, ModuleFileName);
 
-    if (!ImportingInstance.compileModule(ModuleNameLoc,
+    Buffer = ImportingInstance.compileModule(ModuleNameLoc,
                                          Module->getTopLevelModuleName(),
-                                         ModuleFileName, *Instance)) {
+                                         ModuleFileName, *Instance);
+
+    if (!Buffer) {
       ImportingInstance.getDiagnostics().Report(ModuleNameLoc,
                                                 diag::err_module_not_built)
           << Module->Name << SourceRange(ImportLoc, ModuleNameLoc);
@@ -1454,6 +1465,12 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
     }
   }
 
+  time_t ModTime =
+      ImportingInstance.getModuleCache().write(ModuleFileName, *Buffer);
+
+  // TODO: Write to in-memory module cache here.
+  (void)ModTime;
+
   // The module is built successfully, we can update its timestamp now.
   if (ImportingInstance.getPreprocessor()
           .getHeaderSearchInfo()
@@ -2195,8 +2212,9 @@ void CompilerInstance::createModuleFromSource(SourceLocation ImportLoc,
   // output is nondeterministic (as .pcm files refer to each other by name).
   // Can this affect the output in any way?
   SmallString<128> ModuleFileName;
+  int FD;
   if (std::error_code EC = llvm::sys::fs::createTemporaryFile(
-          CleanModuleName, "pcm", ModuleFileName)) {
+          CleanModuleName, "pcm", FD, ModuleFileName)) {
     getDiagnostics().Report(ImportLoc, diag::err_fe_unable_to_open_output)
         << ModuleFileName << EC.message();
     return;
@@ -2224,12 +2242,14 @@ void CompilerInstance::createModuleFromSource(SourceLocation ImportLoc,
   Other->DeleteBuiltModules = false;
 
   // Build the module, inheriting any modules that we've built locally.
-  bool Success = compileModule(ImportLoc, ModuleName, ModuleFileName, *Other);
-
+  std::unique_ptr<llvm::MemoryBuffer> Buffer =
+      compileModule(ImportLoc, ModuleName, ModuleFileName, *Other);
+  llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
   BuiltModules = std::move(Other->BuiltModules);
 
-  if (Success) {
+  if (Buffer) {
     BuiltModules[std::string(ModuleName)] = std::string(ModuleFileName);
+    OS.write(Buffer->getBufferStart(), Buffer->getBufferSize());
     llvm::sys::RemoveFileOnSignal(ModuleFileName);
   }
 }
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index e5eaab0da7adb..42f1ae3d83ed3 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -188,7 +188,8 @@ bool GeneratePCHAction::BeginSourceFileAction(CompilerInstance &CI) {
 std::vector<std::unique_ptr<ASTConsumer>>
 GenerateModuleAction::CreateMultiplexConsumer(CompilerInstance &CI,
                                               StringRef InFile) {
-  std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile);
+  if (!OS)
+    OS = CreateOutputFile(CI, InFile);
   if (!OS)
     return {};
 
diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp
index 658da6e3b7145..718d6c99eff07 100644
--- a/clang/lib/Serialization/ModuleCache.cpp
+++ b/clang/lib/Serialization/ModuleCache.cpp
@@ -101,6 +101,32 @@ void clang::maybePruneImpl(StringRef Path, time_t PruneInterval,
   }
 }
 
+time_t clang::writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer) {
+  StringRef Extension = llvm::sys::path::extension(Path);
+  SmallString<128> ModelPath =
+      StringRef(Path).drop_back(Extension.size());
+  ModelPath += "-%%%%%%%%";
+  ModelPath += Extension;
+  ModelPath += ".tmp";
+
+  auto ModTime = std::chrono::system_clock::now();
+
+  SmallString<128> TmpPath;
+  int FD;
+  if (llvm::sys::fs::createUniqueFile(ModelPath, FD, TmpPath))
+    return 0;
+
+  {
+    llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
+    OS << Buffer.getBuffer();
+    llvm::sys::fs::setLastAccessAndModificationTime(FD, ModTime);
+  }
+
+  llvm::sys::fs::rename(TmpPath, Path);
+
+  return std::chrono::system_clock::to_time_t(ModTime);
+}
+
 namespace {
 class CrossProcessModuleCache : public ModuleCache {
   InMemoryModuleCache InMemory;
@@ -157,6 +183,13 @@ class CrossProcessModuleCache : public ModuleCache {
     maybePruneImpl(Path, PruneInterval, PruneAfter);
   }
 
+  time_t write(StringRef Path, llvm::MemoryBufferRef Buffer) override {
+    // This is a compiler-internal input/output, let's bypass the sandbox.
+    auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
+
+    return writeImpl(Path, Buffer);
+  }
+
   InMemoryModuleCache &getInMemoryModuleCache() override { return InMemory; }
   const InMemoryModuleCache &getInMemoryModuleCache() const override {
     return InMemory;

>From b64dcd6706c2aaa70e8b562239dcdb167d44151b Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Fri, 20 Mar 2026 21:09:27 -0700
Subject: [PATCH 6/7] Write ModTime to in-memory module cache

---
 clang/include/clang/Serialization/ASTWriter.h |  5 +---
 .../clang/Serialization/InMemoryModuleCache.h | 14 ++++++---
 clang/lib/Frontend/CompilerInstance.cpp       |  4 +--
 clang/lib/Serialization/ASTWriter.cpp         |  8 +----
 clang/lib/Serialization/GeneratePCH.cpp       | 12 +++-----
 .../lib/Serialization/InMemoryModuleCache.cpp | 18 ++++++++++--
 clang/lib/Serialization/ModuleManager.cpp     | 29 +++++--------------
 .../Serialization/InMemoryModuleCacheTest.cpp | 20 ++++++-------
 8 files changed, 51 insertions(+), 59 deletions(-)

diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h
index be2fbdf1ade83..7b8376ada731e 100644
--- a/clang/include/clang/Serialization/ASTWriter.h
+++ b/clang/include/clang/Serialization/ASTWriter.h
@@ -723,8 +723,7 @@ class ASTWriter : public ASTDeserializationListener,
   /// the module but currently is merely a random 32-bit number.
   ASTFileSignature WriteAST(llvm::PointerUnion<Sema *, Preprocessor *> Subject,
                             StringRef OutputFile, Module *WritingModule,
-                            StringRef isysroot,
-                            bool ShouldCacheASTInMemory = false);
+                            StringRef isysroot);
 
   /// Emit a token.
   void AddToken(const Token &Tok, RecordDataImpl &Record);
@@ -1002,7 +1001,6 @@ class PCHGenerator : public SemaConsumer {
   llvm::BitstreamWriter Stream;
   ASTWriter Writer;
   bool AllowASTWithErrors;
-  bool ShouldCacheASTInMemory;
 
 protected:
   ASTWriter &getWriter() { return Writer; }
@@ -1024,7 +1022,6 @@ class PCHGenerator : public SemaConsumer {
                ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
                bool AllowASTWithErrors = false, bool IncludeTimestamps = true,
                bool BuildingImplicitModule = false,
-               bool ShouldCacheASTInMemory = false,
                bool GeneratingReducedBMI = false);
   ~PCHGenerator() override;
 
diff --git a/clang/include/clang/Serialization/InMemoryModuleCache.h b/clang/include/clang/Serialization/InMemoryModuleCache.h
index fc3ba334fc64d..170aafcc4741c 100644
--- a/clang/include/clang/Serialization/InMemoryModuleCache.h
+++ b/clang/include/clang/Serialization/InMemoryModuleCache.h
@@ -30,14 +30,16 @@ class InMemoryModuleCache : public llvm::RefCountedBase<InMemoryModuleCache> {
   struct PCM {
     std::unique_ptr<llvm::MemoryBuffer> Buffer;
 
+    time_t ModTime;
+
     /// Track whether this PCM is known to be good (either built or
     /// successfully imported by a CompilerInstance/ASTReader using this
     /// cache).
     bool IsFinal = false;
 
     PCM() = default;
-    PCM(std::unique_ptr<llvm::MemoryBuffer> Buffer)
-        : Buffer(std::move(Buffer)) {}
+    PCM(std::unique_ptr<llvm::MemoryBuffer> Buffer, time_t ModTime)
+        : Buffer(std::move(Buffer)), ModTime(ModTime) {}
   };
 
   /// Cache of buffers.
@@ -64,7 +66,8 @@ class InMemoryModuleCache : public llvm::RefCountedBase<InMemoryModuleCache> {
   /// \post state is Tentative
   /// \return a reference to the buffer as a convenience.
   llvm::MemoryBuffer &addPCM(llvm::StringRef Filename,
-                             std::unique_ptr<llvm::MemoryBuffer> Buffer);
+                             std::unique_ptr<llvm::MemoryBuffer> Buffer,
+                             time_t ModTime);
 
   /// Store a just-built PCM under the Filename.
   ///
@@ -72,7 +75,8 @@ class InMemoryModuleCache : public llvm::RefCountedBase<InMemoryModuleCache> {
   /// \pre state is not Tentative.
   /// \return a reference to the buffer as a convenience.
   llvm::MemoryBuffer &addBuiltPCM(llvm::StringRef Filename,
-                                  std::unique_ptr<llvm::MemoryBuffer> Buffer);
+                                  std::unique_ptr<llvm::MemoryBuffer> Buffer,
+                                  time_t ModTime);
 
   /// Try to remove a buffer from the cache.  No effect if state is Final.
   ///
@@ -90,6 +94,8 @@ class InMemoryModuleCache : public llvm::RefCountedBase<InMemoryModuleCache> {
   /// Get a pointer to the pCM if it exists; else nullptr.
   llvm::MemoryBuffer *lookupPCM(llvm::StringRef Filename) const;
 
+  const std::time_t *lookupPCMModTime(llvm::StringRef Filename) const;
+
   /// Check whether the PCM is final and has been shown to work.
   ///
   /// \return true iff state is Final.
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 7647f80c2f96a..122df21338175 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1468,8 +1468,8 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
   time_t ModTime =
       ImportingInstance.getModuleCache().write(ModuleFileName, *Buffer);
 
-  // TODO: Write to in-memory module cache here.
-  (void)ModTime;
+  ImportingInstance.getModuleCache().getInMemoryModuleCache().addBuiltPCM(
+      ModuleFileName, std::move(Buffer), ModTime);
 
   // The module is built successfully, we can update its timestamp now.
   if (ImportingInstance.getPreprocessor()
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 2b0808ad5ad5e..e6f167ffd5564 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5495,7 +5495,7 @@ time_t ASTWriter::getTimestampForOutput(time_t ModTime) const {
 ASTFileSignature
 ASTWriter::WriteAST(llvm::PointerUnion<Sema *, Preprocessor *> Subject,
                     StringRef OutputFile, Module *WritingModule,
-                    StringRef isysroot, bool ShouldCacheASTInMemory) {
+                    StringRef isysroot) {
   llvm::TimeTraceScope scope("WriteAST", OutputFile);
   WritingAST = true;
 
@@ -5522,12 +5522,6 @@ ASTWriter::WriteAST(llvm::PointerUnion<Sema *, Preprocessor *> Subject,
 
   WritingAST = false;
 
-  if (ShouldCacheASTInMemory) {
-    // Construct MemoryBuffer and update buffer manager.
-    ModCache.getInMemoryModuleCache().addBuiltPCM(
-        OutputFile, llvm::MemoryBuffer::getMemBufferCopy(
-                        StringRef(Buffer.begin(), Buffer.size())));
-  }
   return Signature;
 }
 
diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp
index f8be0e45078db..7769d6170b845 100644
--- a/clang/lib/Serialization/GeneratePCH.cpp
+++ b/clang/lib/Serialization/GeneratePCH.cpp
@@ -28,14 +28,12 @@ PCHGenerator::PCHGenerator(
     const CodeGenOptions &CodeGenOpts,
     ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
     bool AllowASTWithErrors, bool IncludeTimestamps,
-    bool BuildingImplicitModule, bool ShouldCacheASTInMemory,
-    bool GeneratingReducedBMI)
+    bool BuildingImplicitModule,bool GeneratingReducedBMI)
     : PP(PP), Subject(&PP), OutputFile(OutputFile), isysroot(isysroot.str()),
       Buffer(std::move(Buffer)), Stream(this->Buffer->Data),
       Writer(Stream, this->Buffer->Data, ModCache, CodeGenOpts, Extensions,
              IncludeTimestamps, BuildingImplicitModule, GeneratingReducedBMI),
-      AllowASTWithErrors(AllowASTWithErrors),
-      ShouldCacheASTInMemory(ShouldCacheASTInMemory) {
+      AllowASTWithErrors(AllowASTWithErrors) {
   this->Buffer->IsComplete = false;
 }
 
@@ -84,8 +82,7 @@ void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {
   if (AllowASTWithErrors)
     PP.getDiagnostics().getClient()->clear();
 
-  Buffer->Signature = Writer.WriteAST(Subject, OutputFile, Module, isysroot,
-                                      ShouldCacheASTInMemory);
+  Buffer->Signature = Writer.WriteAST(Subject, OutputFile, Module, isysroot);
 
   Buffer->IsComplete = true;
 }
@@ -111,8 +108,7 @@ CXX20ModulesGenerator::CXX20ModulesGenerator(Preprocessor &PP,
           std::make_shared<PCHBuffer>(), CodeGenOpts,
           /*Extensions=*/ArrayRef<std::shared_ptr<ModuleFileExtension>>(),
           AllowASTWithErrors, /*IncludeTimestamps=*/false,
-          /*BuildingImplicitModule=*/false, /*ShouldCacheASTInMemory=*/false,
-          GeneratingReducedBMI) {}
+          /*BuildingImplicitModule=*/false, GeneratingReducedBMI) {}
 
 Module *CXX20ModulesGenerator::getEmittingModule(ASTContext &Ctx) {
   Module *M = Ctx.getCurrentNamedModule();
diff --git a/clang/lib/Serialization/InMemoryModuleCache.cpp b/clang/lib/Serialization/InMemoryModuleCache.cpp
index d35fa2a807f4d..67e8411affe62 100644
--- a/clang/lib/Serialization/InMemoryModuleCache.cpp
+++ b/clang/lib/Serialization/InMemoryModuleCache.cpp
@@ -23,19 +23,23 @@ InMemoryModuleCache::getPCMState(llvm::StringRef Filename) const {
 
 llvm::MemoryBuffer &
 InMemoryModuleCache::addPCM(llvm::StringRef Filename,
-                            std::unique_ptr<llvm::MemoryBuffer> Buffer) {
-  auto Insertion = PCMs.insert(std::make_pair(Filename, std::move(Buffer)));
+                            std::unique_ptr<llvm::MemoryBuffer> Buffer,
+                            time_t ModTime) {
+  auto Insertion =
+      PCMs.insert(std::make_pair(Filename, PCM(std::move(Buffer), ModTime)));
   assert(Insertion.second && "Already has a PCM");
   return *Insertion.first->second.Buffer;
 }
 
 llvm::MemoryBuffer &
 InMemoryModuleCache::addBuiltPCM(llvm::StringRef Filename,
-                                 std::unique_ptr<llvm::MemoryBuffer> Buffer) {
+                                 std::unique_ptr<llvm::MemoryBuffer> Buffer,
+                                 time_t ModTime) {
   auto &PCM = PCMs[Filename];
   assert(!PCM.IsFinal && "Trying to override finalized PCM?");
   assert(!PCM.Buffer && "Trying to override tentative PCM?");
   PCM.Buffer = std::move(Buffer);
+  PCM.ModTime = ModTime;
   PCM.IsFinal = true;
   return *PCM.Buffer;
 }
@@ -48,6 +52,14 @@ InMemoryModuleCache::lookupPCM(llvm::StringRef Filename) const {
   return I->second.Buffer.get();
 }
 
+const std::time_t *
+InMemoryModuleCache::lookupPCMModTime(llvm::StringRef Filename) const {
+  auto I = PCMs.find(Filename);
+  if (I == PCMs.end())
+    return nullptr;
+  return &I->second.ModTime;
+}
+
 bool InMemoryModuleCache::isPCMFinal(llvm::StringRef Filename) const {
   return getPCMState(Filename) == Final;
 }
diff --git a/clang/lib/Serialization/ModuleManager.cpp b/clang/lib/Serialization/ModuleManager.cpp
index ed7b6cf67674e..7c7f2b0dc976d 100644
--- a/clang/lib/Serialization/ModuleManager.cpp
+++ b/clang/lib/Serialization/ModuleManager.cpp
@@ -157,31 +157,18 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
   llvm::MemoryBuffer *ModuleBuffer = nullptr;
   std::unique_ptr<llvm::MemoryBuffer> NewFileBuffer = nullptr;
   if (std::unique_ptr<llvm::MemoryBuffer> Buffer = lookupBuffer(FileName)) {
+    auto Entry = FileMgr.getOptionalFileRef(FileName, /*OpenFile=*/false,
+                                          /*CacheFailure=*/false);
     // The buffer was already provided for us.
     ModuleBuffer = &getModuleCache().getInMemoryModuleCache().addBuiltPCM(
-        FileName, std::move(Buffer));
+        FileName, std::move(Buffer), Entry->getModificationTime());
   } else if (llvm::MemoryBuffer *Buffer =
                  getModuleCache().getInMemoryModuleCache().lookupPCM(
                      FileName)) {
     ModuleBuffer = Buffer;
-    if (!FileName.getImplicitModuleSuffixLength()) {
-      // Explicitly-built PCM files maintain consistency via mtime/size
-      // expectations on their imports. Even if we've previously successfully
-      // loaded a PCM file and stored it in the in-memory module cache, that
-      // does not mean its mtime/size matches current importer's expectations.
-      // Get that information so that it can be checked below.
-      // FIXME: Even though this FileManager access is likely already cached, we
-      // should store this directly in the in-memory module cache.
-      OptionalFileEntryRef Entry =
-          FileMgr.getOptionalFileRef(FileName, /*OpenFile=*/true,
-                                     /*CacheFailure=*/false);
-      if (!Entry) {
-        ErrorStr = "module file not found";
-        return Missing;
-      }
-      ModTime = Entry->getModificationTime();
-      Size = Entry->getSize();
-    }
+    Size = Buffer->getBufferSize();
+    ModTime =
+        *getModuleCache().getInMemoryModuleCache().lookupPCMModTime(FileName);
   } else if (getModuleCache().getInMemoryModuleCache().shouldBuildPCM(
                  FileName)) {
     // Report that the module is out of date, since we tried (and failed) to
@@ -242,8 +229,8 @@ ModuleManager::AddModuleResult ModuleManager::addModule(
     return OutOfDate;
 
   if (NewFileBuffer)
-    getModuleCache().getInMemoryModuleCache().addPCM(FileName,
-                                                     std::move(NewFileBuffer));
+    getModuleCache().getInMemoryModuleCache().addPCM(
+        FileName, std::move(NewFileBuffer), ModTime);
 
   // We're keeping this module. Store it in the map.
   Module = Modules[*FileKey] = NewModule.get();
diff --git a/clang/unittests/Serialization/InMemoryModuleCacheTest.cpp b/clang/unittests/Serialization/InMemoryModuleCacheTest.cpp
index ed5e1538eba74..3cf63f6b77701 100644
--- a/clang/unittests/Serialization/InMemoryModuleCacheTest.cpp
+++ b/clang/unittests/Serialization/InMemoryModuleCacheTest.cpp
@@ -39,15 +39,15 @@ TEST(InMemoryModuleCacheTest, addPCM) {
   auto *RawB = B.get();
 
   InMemoryModuleCache Cache;
-  EXPECT_EQ(RawB, &Cache.addPCM("B", std::move(B)));
+  EXPECT_EQ(RawB, &Cache.addPCM("B", std::move(B), 0));
   EXPECT_EQ(InMemoryModuleCache::Tentative, Cache.getPCMState("B"));
   EXPECT_EQ(RawB, Cache.lookupPCM("B"));
   EXPECT_FALSE(Cache.isPCMFinal("B"));
   EXPECT_FALSE(Cache.shouldBuildPCM("B"));
 
 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
-  EXPECT_DEATH(Cache.addPCM("B", getBuffer(2)), "Already has a PCM");
-  EXPECT_DEATH(Cache.addBuiltPCM("B", getBuffer(2)),
+  EXPECT_DEATH(Cache.addPCM("B", getBuffer(2), 0), "Already has a PCM");
+  EXPECT_DEATH(Cache.addBuiltPCM("B", getBuffer(2), 0),
                "Trying to override tentative PCM");
 #endif
 }
@@ -57,15 +57,15 @@ TEST(InMemoryModuleCacheTest, addBuiltPCM) {
   auto *RawB = B.get();
 
   InMemoryModuleCache Cache;
-  EXPECT_EQ(RawB, &Cache.addBuiltPCM("B", std::move(B)));
+  EXPECT_EQ(RawB, &Cache.addBuiltPCM("B", std::move(B), 0));
   EXPECT_EQ(InMemoryModuleCache::Final, Cache.getPCMState("B"));
   EXPECT_EQ(RawB, Cache.lookupPCM("B"));
   EXPECT_TRUE(Cache.isPCMFinal("B"));
   EXPECT_FALSE(Cache.shouldBuildPCM("B"));
 
 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
-  EXPECT_DEATH(Cache.addPCM("B", getBuffer(2)), "Already has a PCM");
-  EXPECT_DEATH(Cache.addBuiltPCM("B", getBuffer(2)),
+  EXPECT_DEATH(Cache.addPCM("B", getBuffer(2), 0), "Already has a PCM");
+  EXPECT_DEATH(Cache.addBuiltPCM("B", getBuffer(2), 0),
                "Trying to override finalized PCM");
 #endif
 }
@@ -79,7 +79,7 @@ TEST(InMemoryModuleCacheTest, tryToDropPCM) {
 
   InMemoryModuleCache Cache;
   EXPECT_EQ(InMemoryModuleCache::Unknown, Cache.getPCMState("B"));
-  EXPECT_EQ(RawB1, &Cache.addPCM("B", std::move(B1)));
+  EXPECT_EQ(RawB1, &Cache.addPCM("B", std::move(B1), 0));
   EXPECT_FALSE(Cache.tryToDropPCM("B"));
   EXPECT_EQ(nullptr, Cache.lookupPCM("B"));
   EXPECT_EQ(InMemoryModuleCache::ToBuild, Cache.getPCMState("B"));
@@ -87,14 +87,14 @@ TEST(InMemoryModuleCacheTest, tryToDropPCM) {
   EXPECT_TRUE(Cache.shouldBuildPCM("B"));
 
 #if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
-  EXPECT_DEATH(Cache.addPCM("B", getBuffer(2)), "Already has a PCM");
+  EXPECT_DEATH(Cache.addPCM("B", getBuffer(2), 0), "Already has a PCM");
   EXPECT_DEATH(Cache.tryToDropPCM("B"),
                "PCM to remove is scheduled to be built");
   EXPECT_DEATH(Cache.finalizePCM("B"), "Trying to finalize a dropped PCM");
 #endif
 
   // Add a new one.
-  EXPECT_EQ(RawB2, &Cache.addBuiltPCM("B", std::move(B2)));
+  EXPECT_EQ(RawB2, &Cache.addBuiltPCM("B", std::move(B2), 0));
   EXPECT_TRUE(Cache.isPCMFinal("B"));
 
   // Can try to drop again, but this should error and do nothing.
@@ -108,7 +108,7 @@ TEST(InMemoryModuleCacheTest, finalizePCM) {
 
   InMemoryModuleCache Cache;
   EXPECT_EQ(InMemoryModuleCache::Unknown, Cache.getPCMState("B"));
-  EXPECT_EQ(RawB, &Cache.addPCM("B", std::move(B)));
+  EXPECT_EQ(RawB, &Cache.addPCM("B", std::move(B), 0));
 
   // Call finalize.
   Cache.finalizePCM("B");

>From 39dfef45b5c6b389e42f4a927235571c8f3d49d6 Mon Sep 17 00:00:00 2001
From: Jan Svoboda <jan_svoboda at apple.com>
Date: Sat, 21 Mar 2026 10:54:10 -0700
Subject: [PATCH 7/7] Foo

---
 clang/lib/Frontend/CompilerInstance.cpp | 20 ++++++++++++++++----
 clang/lib/Serialization/ModuleCache.cpp | 19 ++++++++++++++++---
 2 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 122df21338175..8d6c4a17a1f06 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1465,11 +1465,23 @@ static bool compileModuleImpl(CompilerInstance &ImportingInstance,
     }
   }
 
-  time_t ModTime =
-      ImportingInstance.getModuleCache().write(ModuleFileName, *Buffer);
+  {
+    auto Out = ImportingInstance.getOutputManager().createFile(ModuleFileName.str());
+    if (!Out) {
+      llvm::consumeError(Out.takeError());
+      return false;
+    }
+    *Out << Buffer->getBuffer();
+    if (llvm::Error Err = Out->keep()) {
+      llvm::consumeError(Out.takeError());
+      return false;
+    }
+  }
+  // time_t ModTime =
+  //     ImportingInstance.getModuleCache().write(ModuleFileName, *Buffer);
 
-  ImportingInstance.getModuleCache().getInMemoryModuleCache().addBuiltPCM(
-      ModuleFileName, std::move(Buffer), ModTime);
+  // ImportingInstance.getModuleCache().getInMemoryModuleCache().addBuiltPCM(
+  //     ModuleFileName, std::move(Buffer), 0);
 
   // The module is built successfully, we can update its timestamp now.
   if (ImportingInstance.getPreprocessor()
diff --git a/clang/lib/Serialization/ModuleCache.cpp b/clang/lib/Serialization/ModuleCache.cpp
index 718d6c99eff07..3f5915a717d69 100644
--- a/clang/lib/Serialization/ModuleCache.cpp
+++ b/clang/lib/Serialization/ModuleCache.cpp
@@ -111,18 +111,31 @@ time_t clang::writeImpl(StringRef Path, llvm::MemoryBufferRef Buffer) {
 
   auto ModTime = std::chrono::system_clock::now();
 
+  if (llvm::sys::fs::create_directories(llvm::sys::path::parent_path(ModelPath))) {
+    llvm::errs() << "failed to create_directories in clang::writeImpl\n";
+    return 0;
+  }
+
   SmallString<128> TmpPath;
   int FD;
-  if (llvm::sys::fs::createUniqueFile(ModelPath, FD, TmpPath))
+  if (llvm::sys::fs::createUniqueFile(ModelPath, FD, TmpPath)) {
+    llvm::errs() << "failed to createUniqueFile in clang::writeImpl\n";
     return 0;
+  }
 
   {
     llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
     OS << Buffer.getBuffer();
-    llvm::sys::fs::setLastAccessAndModificationTime(FD, ModTime);
+    if (llvm::sys::fs::setLastAccessAndModificationTime(FD, ModTime)) {
+      llvm::errs() << "failed to setLastAccessAndModificationTime in clang::writeImpl\n";
+      return 0;
+    }
   }
 
-  llvm::sys::fs::rename(TmpPath, Path);
+  if (llvm::sys::fs::rename(TmpPath, Path)) {
+    llvm::errs() << "failed to rename in clang::writeImpl\n";
+    return 0;
+  }
 
   return std::chrono::system_clock::to_time_t(ModTime);
 }



More information about the cfe-commits mailing list