Index: clang/include/clang/Basic/FileManager.h =================================================================== --- clang/include/clang/Basic/FileManager.h +++ clang/include/clang/Basic/FileManager.h @@ -157,6 +157,10 @@ /// The virtual files that we have allocated. SmallVector, 4> VirtualFileEntries; + /// A set of files that bypass the maps and uniquing. They can have + /// conflicting filenames. + SmallVector, 0> BypassFileEntries; + /// A cache that maps paths to directory entries (either real or /// virtual) we have looked up, or an error that occurred when we looked up /// the directory. @@ -305,6 +309,16 @@ const FileEntry *getVirtualFile(StringRef Filename, off_t Size, time_t ModificationTime); + /// Retrieve a FileEntry that bypasses VFE, which is expected to be a virtual + /// file entry, to access the real file. The returned FileEntry will have + /// the same filename as FE but a different identity and its own stat. + /// + /// This should be used only for rare error recovery paths because it + /// bypasses all mapping and uniquing, blindly creating a new FileEntry. + /// There is no attempt to deduplicate these; if you bypass the same file + /// twice, you get two new file entries. + const FileEntry *getBypassFile(const FileEntry &VFE); + /// Open the specified file as a MemoryBuffer, returning a new /// MemoryBuffer if successful, otherwise returning null. llvm::ErrorOr> @@ -348,11 +362,6 @@ void GetUniqueIDMapping( SmallVectorImpl &UIDToFiles) const; - /// Modifies the size and modification time of a previously created - /// FileEntry. Use with caution. - static void modifyFileEntry(FileEntry *File, off_t Size, - time_t ModificationTime); - /// Retrieve the canonical name for a given directory. /// /// This is a very expensive operation, despite its results being cached, Index: clang/include/clang/Basic/SourceManager.h =================================================================== --- clang/include/clang/Basic/SourceManager.h +++ clang/include/clang/Basic/SourceManager.h @@ -952,11 +952,12 @@ return false; } - /// Disable overridding the contents of a file, previously enabled - /// with #overrideFileContents. + /// Bypass the overridden contents of a file. This creates a new FileEntry + /// and initializes the content cache for it. Returns nullptr if there is no + /// such file in the filesystem. /// /// This should be called before parsing has begun. - void disableFileContentsOverride(const FileEntry *File); + const FileEntry *bypassFileContentsOverride(const FileEntry &File); /// Specify that a file is transient. void setFileIsTransient(const FileEntry *SourceFile); Index: clang/lib/Basic/FileManager.cpp =================================================================== --- clang/lib/Basic/FileManager.cpp +++ clang/lib/Basic/FileManager.cpp @@ -389,6 +389,24 @@ return UFE; } +const FileEntry *FileManager::getBypassFile(const FileEntry &VFE) { + // Stat of the file and return nullptr if it doesn't exist. + llvm::vfs::Status Status; + if (getStatValue(VFE.Name, Status, /*isFile=*/true, /*F=*/nullptr)) + return nullptr; + + // Fill it in from the stat. + BypassFileEntries.push_back(std::make_unique()); + auto &BFE = *BypassFileEntries.back(); + BFE.Name = VFE.Name; + BFE.Size = Status.getSize(); + BFE.Dir = VFE.Dir; + BFE.ModTime = llvm::sys::toTimeT(Status.getLastModificationTime()); + BFE.UID = NextFileUID++; + BFE.IsValid = true; + return &BFE; +} + bool FileManager::FixupRelativePath(SmallVectorImpl &path) const { StringRef pathRef(path.data(), path.size()); @@ -532,12 +550,6 @@ UIDToFiles[VFE->getUID()] = VFE.get(); } -void FileManager::modifyFileEntry(FileEntry *File, - off_t Size, time_t ModificationTime) { - File->Size = Size; - File->ModTime = ModificationTime; -} - StringRef FileManager::getCanonicalName(const DirectoryEntry *Dir) { // FIXME: use llvm::sys::fs::canonical() when it gets implemented llvm::DenseMap::iterator Known Index: clang/lib/Basic/SourceManager.cpp =================================================================== --- clang/lib/Basic/SourceManager.cpp +++ clang/lib/Basic/SourceManager.cpp @@ -671,17 +671,17 @@ getOverriddenFilesInfo().OverriddenFiles[SourceFile] = NewFile; } -void SourceManager::disableFileContentsOverride(const FileEntry *File) { - if (!isFileOverridden(File)) - return; +const FileEntry * +SourceManager::bypassFileContentsOverride(const FileEntry &File) { + assert(isFileOverridden(&File)); + auto *BypassFile = FileMgr.getBypassFile(File); - const SrcMgr::ContentCache *IR = getOrCreateContentCache(File); - const_cast(IR)->replaceBuffer(nullptr); - const_cast(IR)->ContentsEntry = IR->OrigEntry; + // If the file can't be found in the FS, give up. + if (!BypassFile) + return nullptr; - assert(OverriddenFilesInfo); - OverriddenFilesInfo->OverriddenFiles.erase(File); - OverriddenFilesInfo->OverriddenFilesWithBuffer.erase(File); + (void)getOrCreateContentCache(BypassFile); + return BypassFile; } void SourceManager::setFileIsTransient(const FileEntry *File) { Index: clang/lib/Serialization/ASTReader.cpp =================================================================== --- clang/lib/Serialization/ASTReader.cpp +++ clang/lib/Serialization/ASTReader.cpp @@ -2315,19 +2315,14 @@ if ((!Overridden && !Transient) && SM.isFileOverridden(File)) { if (Complain) Error(diag::err_fe_pch_file_overridden, Filename); - // After emitting the diagnostic, recover by disabling the override so - // that the original file will be used. - // - // FIXME: This recovery is just as broken as the original state; there may - // be another precompiled module that's using the overridden contents, or - // we might be half way through parsing it. Instead, we should treat the - // overridden contents as belonging to a separate FileEntry. - SM.disableFileContentsOverride(File); - // The FileEntry is a virtual file entry with the size of the contents - // that would override the original contents. Set it to the original's - // size/time. - FileMgr.modifyFileEntry(const_cast(File), - StoredSize, StoredTime); + + // After emitting the diagnostic, bypass the overriding file to recover + // (this creates a separate FileEntry). + File = SM.bypassFileContentsOverride(*File); + if (!File) { + F.InputFilesLoaded[ID - 1] = InputFile::getNotFound(); + return InputFile(); + } } bool IsOutOfDate = false;