https://github.com/Evianaive updated https://github.com/llvm/llvm-project/pull/182750
>From 38699993550ad9bb91f00145ec5244b82d5ff4f2 Mon Sep 17 00:00:00 2001 From: Evianaive <[email protected]> Date: Wed, 25 Feb 2026 12:51:19 +0800 Subject: [PATCH 1/3] [clang-repl] Migrate the file content cache invalidation and macro cleanup logic within the Cling implementation. --- clang/include/clang/Basic/FileManager.h | 9 +++++ clang/include/clang/Basic/SourceManager.h | 4 ++- clang/include/clang/Lex/Preprocessor.h | 5 +++ clang/lib/Basic/FileManager.cpp | 41 +++++++++++++++++++++-- clang/lib/Basic/SourceManager.cpp | 11 ++++++ clang/lib/Lex/PPMacroExpansion.cpp | 13 +++++++ 6 files changed, 79 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Basic/FileManager.h b/clang/include/clang/Basic/FileManager.h index 01ce243e1b5fc..9de7f27167e40 100644 --- a/clang/include/clang/Basic/FileManager.h +++ b/clang/include/clang/Basic/FileManager.h @@ -31,6 +31,7 @@ #include <ctime> #include <map> #include <memory> +#include <set> #include <string> namespace llvm { @@ -107,6 +108,11 @@ class FileManager : public RefCountedBase<FileManager> { /// The canonical names of files and directories . llvm::DenseMap<const void *, llvm::StringRef> CanonicalNames; + std::set<const FileEntry *> FileEntriesToReread; + + /// The canonical names of directories. + llvm::DenseMap<const DirectoryEntry *, llvm::StringRef> CanonicalDirNames; + /// Storage for canonical names that we have computed. llvm::BumpPtrAllocator CanonicalNameStorage; @@ -284,6 +290,9 @@ class FileManager : public RefCountedBase<FileManager> { std::error_code getNoncachedStatValue(StringRef Path, llvm::vfs::Status &Result); + /// Remove the real file \p Entry from the cache. + void invalidateCache(FileEntryRef Entry); + /// If path is not absolute and FileSystemOptions set the working /// directory, the path is modified to be relative to the given /// working directory. diff --git a/clang/include/clang/Basic/SourceManager.h b/clang/include/clang/Basic/SourceManager.h index bc9e97863556d..334854c5ba798 100644 --- a/clang/include/clang/Basic/SourceManager.h +++ b/clang/include/clang/Basic/SourceManager.h @@ -256,7 +256,7 @@ class alignas(8) ContentCache { /// Set the buffer. void setBuffer(std::unique_ptr<llvm::MemoryBuffer> B) { - IsBufferInvalid = false; + IsBufferInvalid = !B; Buffer = std::move(B); } @@ -849,6 +849,8 @@ class SourceManager : public RefCountedBase<SourceManager> { void clearIDTables(); + void invalidateCache(FileID FID); + /// Initialize this source manager suitably to replay the compilation /// described by \p Old. Requires that \p Old outlive \p *this. void initializeForReplay(const SourceManager &Old); diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index b6e42a6151ac3..4b43ba810ac97 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -1493,6 +1493,11 @@ class Preprocessor { return appendDefMacroDirective(II, MI, MI->getDefinitionLoc()); } + /// Remove a IdentifierInfo and MacroDirective from the history. + /// Given an IdentifierInfo and a MacroDirective we can remove them from + /// the macros vector. + void removeMacro(IdentifierInfo *II, MacroDirective *MD); + /// Set a MacroDirective that was loaded from a PCH file. void setLoadedMacroDirective(IdentifierInfo *II, MacroDirective *ED, MacroDirective *MD); diff --git a/clang/lib/Basic/FileManager.cpp b/clang/lib/Basic/FileManager.cpp index 76f732865d6ac..278bb243e5092 100644 --- a/clang/lib/Basic/FileManager.cpp +++ b/clang/lib/Basic/FileManager.cpp @@ -217,7 +217,25 @@ llvm::Expected<FileEntryRef> FileManager::getFileRef(StringRef Filename, // See if there is already an entry in the map. auto SeenFileInsertResult = SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory}); - if (!SeenFileInsertResult.second) { + + auto *NamedFileEnt = &*SeenFileInsertResult.first; + + const FileEntry *StaleFileEntry = 0; + bool needsRereading = false; + if (NamedFileEnt && NamedFileEnt->getValue()) { + FileEntryRef::MapValue Value = *NamedFileEnt->getValue(); + if (isa<FileEntry *>(Value.V)) { + auto found = FileEntriesToReread.find(cast<FileEntry *>(Value.V)); + if (found != FileEntriesToReread.end()) { + needsRereading = true; + StaleFileEntry = *found; + FileEntriesToReread.erase(found); + } + } + } + + // See if there is already an entry in the map. + if (!SeenFileInsertResult.second && !needsRereading) { if (!SeenFileInsertResult.first->second) return llvm::errorCodeToError( SeenFileInsertResult.first->second.getError()); @@ -226,8 +244,6 @@ llvm::Expected<FileEntryRef> FileManager::getFileRef(StringRef Filename, // We've not seen this before. Fill it in. ++NumFileCacheMisses; - auto *NamedFileEnt = &*SeenFileInsertResult.first; - assert(!NamedFileEnt->second && "should be newly-created"); // Get the null-terminated file name as stored as the key of the // SeenFileEntries map. @@ -353,6 +369,20 @@ llvm::Expected<FileEntryRef> FileManager::getFileRef(StringRef Filename, // We should still fill the path even if we aren't opening the file. fillRealPathName(UFE, InterndFileName); } + + if (StaleFileEntry) { + // Find occurrences of old FileEntry; update with new one: + for (auto &fe : SeenFileEntries) { + if (fe.getValue()) { + FileEntryRef::MapValue Value = *fe.getValue(); + if (isa<FileEntry *>(Value.V) && + cast<FileEntry *>(Value.V) == StaleFileEntry) { + fe.setValue(FileEntryRef::MapValue(*UFE, DirInfo)); + } + } + } + } + return ReturnedRef; } @@ -614,6 +644,11 @@ FileManager::getNoncachedStatValue(StringRef Path, return std::error_code(); } +void FileManager::invalidateCache(FileEntryRef Entry) { + assert(Entry && "Cannot invalidate a NULL FileEntry"); + FileEntriesToReread.insert(Entry); +} + void FileManager::GetUniqueIDMapping( SmallVectorImpl<OptionalFileEntryRef> &UIDToFiles) const { UIDToFiles.clear(); diff --git a/clang/lib/Basic/SourceManager.cpp b/clang/lib/Basic/SourceManager.cpp index b6cc6ec9365f5..2ec21f6157b82 100644 --- a/clang/lib/Basic/SourceManager.cpp +++ b/clang/lib/Basic/SourceManager.cpp @@ -358,6 +358,17 @@ bool SourceManager::isMainFile(const FileEntry &SourceFile) { return false; } +void SourceManager::invalidateCache(FileID FID) { + OptionalFileEntryRef Entry = getFileEntryRefForID(FID); + if (!Entry) + return; + if (ContentCache *&E = FileInfos[*Entry]) { + E->setBuffer(nullptr); + E = 0; + } + getFileManager().invalidateCache(*Entry); +} + void SourceManager::initializeForReplay(const SourceManager &Old) { assert(MainFileID.isInvalid() && "expected uninitialized SourceManager"); diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 20b8fe585a007..77e8fca63c3fc 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -91,6 +91,19 @@ void Preprocessor::appendMacroDirective(IdentifierInfo *II, MacroDirective *MD){ II->setChangedSinceDeserialization(); } +void Preprocessor::removeMacro(IdentifierInfo *II, MacroDirective *MD) { + assert(II && MD); + II->setHasMacroDefinition(false); + CurSubmoduleState->Macros.erase(II); + if (MacroDirective *prevMD = MD->getPrevious()) { + // Avoid assertion in appendMacroDirective. + MacroDirective *prevPrevMD = prevMD->getPrevious(); + prevMD->setPrevious(0); + appendMacroDirective(II, prevMD); + prevMD->setPrevious(prevPrevMD); + } +} + void Preprocessor::setLoadedMacroDirective(IdentifierInfo *II, MacroDirective *ED, MacroDirective *MD) { >From 6b61323e768feac4a3c6c14e5979e58e007be028 Mon Sep 17 00:00:00 2001 From: Evianaive <[email protected]> Date: Wed, 25 Feb 2026 12:57:53 +0800 Subject: [PATCH 2/3] [clang-repl] Add PreProcessor Track to implement undo of include and macro Add PreProcessor tracker. And IncludedFiles and MacroDirectiveInfos in PartialTranslationUnit. Wrap CleanUpPTU with proper semantics and rename the original function to CleanUpTU fix move --- .../Interpreter/PartialTranslationUnit.h | 35 ++++++ clang/lib/Interpreter/IncrementalParser.cpp | 118 +++++++++++++++++- clang/lib/Interpreter/IncrementalParser.h | 7 +- clang/lib/Interpreter/Interpreter.cpp | 2 +- 4 files changed, 158 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Interpreter/PartialTranslationUnit.h b/clang/include/clang/Interpreter/PartialTranslationUnit.h index c878e139fe70d..18eb21e5aa281 100644 --- a/clang/include/clang/Interpreter/PartialTranslationUnit.h +++ b/clang/include/clang/Interpreter/PartialTranslationUnit.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_INTERPRETER_PARTIALTRANSLATIONUNIT_H #define LLVM_CLANG_INTERPRETER_PARTIALTRANSLATIONUNIT_H +#include "clang/Basic/FileEntry.h" #include <memory> namespace llvm { @@ -22,6 +23,8 @@ class Module; namespace clang { +class IdentifierInfo; +class MacroDirective; class TranslationUnitDecl; /// The class keeps track of various objects created as part of processing @@ -34,6 +37,38 @@ struct PartialTranslationUnit { bool operator==(const PartialTranslationUnit &other) { return other.TUPart == TUPart && other.TheModule == TheModule; } + + ///\brief Each macro pair (is this the same as for decls?)came + /// through different interface at + /// different time. We are being conservative and we want to keep all the + /// call sequence that originally occurred in clang. + /// + struct MacroDirectiveInfo { + // We need to store both the IdentifierInfo and the MacroDirective + // because the Preprocessor stores the macros in a DenseMap<II, MD>. + IdentifierInfo *II; + const MacroDirective *MD; + MacroDirectiveInfo(IdentifierInfo *II, const MacroDirective *MD) + : II(II), MD(MD) {} + inline bool operator==(const MacroDirectiveInfo &rhs) const { + return II == rhs.II && MD == rhs.MD; + } + inline bool operator!=(const MacroDirectiveInfo &rhs) const { + return !operator==(rhs); + } + }; + // Intentionally use struct instead of pair because we don't need default + // init. + // Add macro decls to be able to revert them for error recovery. + typedef llvm::SmallVector<MacroDirectiveInfo, 2> MacroDirectiveInfoQueue; + + ///\brief All seen macros. + /// + MacroDirectiveInfoQueue MacroDirectiveInfos; + + /// Files that were #included during this PTU's parsing. + /// Used to reset HeaderFileInfo on Undo. + std::vector<FileEntryRef> IncludedFiles; }; } // namespace clang diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp index bf08911e23533..73c0b078467bb 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -16,6 +16,7 @@ #include "clang/AST/DeclContextInternals.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Interpreter/PartialTranslationUnit.h" +#include "clang/Lex/PPCallbacks.h" #include "clang/Parse/Parser.h" #include "clang/Sema/Sema.h" #include "llvm/IR/Module.h" @@ -28,6 +29,61 @@ namespace clang { +class IncrementalPreProcessorTracker : public PPCallbacks { + std::vector<FileEntryRef> IncludedFiles; + PartialTranslationUnit::MacroDirectiveInfoQueue MacroDirectives; + void MacroDirective(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD) { + PartialTranslationUnit::MacroDirectiveInfo MDE( + MacroNameTok.getIdentifierInfo(), MD); + MacroDirectives.push_back(MDE); + } + +public: + explicit IncrementalPreProcessorTracker() {} + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, + OptionalFileEntryRef File, StringRef SearchPath, + StringRef RelativePath, const Module *SuggestedModule, + bool ModuleImported, + SrcMgr::CharacteristicKind FileType) override { + + if (File) { + IncludedFiles.push_back(*File); + } + } + + /// \name PPCallbacks overrides + /// Macro support + void MacroDefined(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD) final { + MacroDirective(MacroNameTok, MD); + } + + /// \name PPCallbacks overrides + /// Macro support + void MacroUndefined(const clang::Token &MacroNameTok, + const clang::MacroDefinition & /*MD*/, + const clang::MacroDirective *Undef) final { + if (Undef) + MacroDirective(MacroNameTok, Undef); + } + + /// Move out tracked files after Parse completes + std::vector<FileEntryRef> takeFiles() { return std::move(IncludedFiles); } + + /// Move out tracked macros after Parse completes + PartialTranslationUnit::MacroDirectiveInfoQueue takeMacros() { + return std::move(MacroDirectives); + } + + void reset() { + IncludedFiles.clear(); + MacroDirectives.clear(); + } +}; + // IncrementalParser::IncrementalParser() {} IncrementalParser::IncrementalParser(CompilerInstance &Instance, @@ -42,6 +98,11 @@ IncrementalParser::IncrementalParser(CompilerInstance &Instance, External->StartTranslationUnit(Consumer); P->Initialize(); + + // track files that are included when parse to support undo + auto Tracker = std::make_unique<IncrementalPreProcessorTracker>(); + PreProcessorTracker = Tracker.get(); + S.getPreprocessor().addPPCallbacks(std::move(Tracker)); } IncrementalParser::~IncrementalParser() { P.reset(); } @@ -82,7 +143,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() { DiagnosticsEngine &Diags = S.getDiagnostics(); if (Diags.hasErrorOccurred()) { - CleanUpPTU(C.getTranslationUnitDecl()); + CleanUpTU(C.getTranslationUnitDecl()); Diags.Reset(/*soft=*/true); Diags.getClient()->clear(); @@ -109,6 +170,9 @@ IncrementalParser::Parse(llvm::StringRef input) { Preprocessor &PP = S.getPreprocessor(); assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?"); + if (PreProcessorTracker) + PreProcessorTracker->reset(); + std::ostringstream SourceName; SourceName << "input_line_" << InputCount++; @@ -161,7 +225,7 @@ IncrementalParser::Parse(llvm::StringRef input) { return PTU; } -void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) { +void IncrementalParser::CleanUpTU(TranslationUnitDecl *MostRecentTU) { if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) { for (auto &&[Key, List] : *Map) { DeclContextLookupResult R = List.getLookupResult(); @@ -194,6 +258,51 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) { } } +void IncrementalParser::CleanUpPTU( + const PartialTranslationUnit &MostRecentPTU) { + CleanUpTU(MostRecentPTU.TUPart); + + auto &PP = P->getPreprocessor(); + auto &HS = PP.getHeaderSearchInfo(); + auto &FM = PP.getFileManager(); + + auto &MacroDirectives = PTUs.back().MacroDirectiveInfos; + bool Successful = true; + if (!MacroDirectives.empty()) { + for (auto MI = MacroDirectives.rbegin(), ME = MacroDirectives.rend(); + MI != ME; ++MI) { + // Get rid of the macro definition + auto UnloadMacro = + [&PP](PartialTranslationUnit::MacroDirectiveInfo MacroD) { + const MacroDirective *MD = MacroD.MD; + // Undef the definition + const MacroInfo *MI = MD->getMacroInfo(); + + // If the macro is not defined, this is a noop undef, just return. + if (!MI) + return false; + + // Remove the pair from the macros + PP.removeMacro(MacroD.II, const_cast<MacroDirective *>(MacroD.MD)); + + return true; + }; + Successful = UnloadMacro(*MI) && Successful; + } + } + if (!PTUs.back().IncludedFiles.empty()) { + for (FileEntryRef FE : PTUs.back().IncludedFiles) { + HeaderFileInfo &HFI = HS.getFileInfo(FE); + HFI.IsLocallyIncluded = false; + HFI.isPragmaOnce = false; + HFI.LazyControllingMacro = LazyIdentifierInfoPtr(); + HFI.IsValid = false; + + FM.invalidateCache(FE); + } + } +} + PartialTranslationUnit & IncrementalParser::RegisterPTU(TranslationUnitDecl *TU, std::unique_ptr<llvm::Module> M /*={}*/) { @@ -213,6 +322,11 @@ IncrementalParser::RegisterPTU(TranslationUnitDecl *TU, LLVM_DEBUG(llvm::dbgs() << ", M=" << LastPTU.TheModule.get() << " (" << LastPTU.TheModule->getName() << ")"); LLVM_DEBUG(llvm::dbgs() << "]\n"); + + if (PreProcessorTracker) { + LastPTU.IncludedFiles = PreProcessorTracker->takeFiles(); + LastPTU.MacroDirectiveInfos = PreProcessorTracker->takeMacros(); + } return LastPTU; } } // end namespace clang diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h index 9b042bc494efb..df508eb24a6f5 100644 --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -30,6 +30,7 @@ class Parser; class Sema; class TranslationUnitDecl; class IncrementalAction; +class IncrementalPreProcessorTracker; struct PartialTranslationUnit; /// Provides support for incremental compilation. Keeps track of the state @@ -54,6 +55,9 @@ class IncrementalParser { std::list<PartialTranslationUnit> &PTUs; + /// Tracks the include files during parsing + IncrementalPreProcessorTracker *PreProcessorTracker; + public: IncrementalParser(CompilerInstance &Instance, IncrementalAction *Act, llvm::Error &Err, std::list<PartialTranslationUnit> &PTUs); @@ -64,8 +68,9 @@ class IncrementalParser { /// \c TranslationUnitDecl. virtual llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input); - void CleanUpPTU(TranslationUnitDecl *MostRecentTU); + void CleanUpTU(TranslationUnitDecl *MostRecentTU); + void CleanUpPTU(const PartialTranslationUnit &MostRecentPTU); /// Register a PTU produced by Parse. PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU, std::unique_ptr<llvm::Module> M = {}); diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 9c94cfa5ee381..32a5cbc398e95 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -592,7 +592,7 @@ llvm::Error Interpreter::Undo(unsigned N) { return Err; } - IncrParser->CleanUpPTU(PTUs.back().TUPart); + IncrParser->CleanUpPTU(PTUs.back()); PTUs.pop_back(); } return llvm::Error::success(); >From 296872a5303b17922fed4cae463c03d029dc1af5 Mon Sep 17 00:00:00 2001 From: Evianaive <[email protected]> Date: Wed, 25 Feb 2026 12:59:19 +0800 Subject: [PATCH 3/3] [clang-repl] Add Test for undo include and macro --- .../test/Interpreter/Inputs/dynamic-header.h | 9 ++++++ clang/test/Interpreter/code-undo.cpp | 29 +++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 clang/test/Interpreter/Inputs/dynamic-header.h diff --git a/clang/test/Interpreter/Inputs/dynamic-header.h b/clang/test/Interpreter/Inputs/dynamic-header.h new file mode 100644 index 0000000000000..9abc4ef5f0629 --- /dev/null +++ b/clang/test/Interpreter/Inputs/dynamic-header.h @@ -0,0 +1,9 @@ +#ifndef DYNAMIC_HEADER_H +#define DYNAMIC_HEADER_H + +#define MACRO_FROM_HEADER 100 +inline int getDynamicValue() { + return MACRO_FROM_HEADER; +} + +#endif diff --git a/clang/test/Interpreter/code-undo.cpp b/clang/test/Interpreter/code-undo.cpp index 4516910ca3b4f..22bcf1d1dc080 100644 --- a/clang/test/Interpreter/code-undo.cpp +++ b/clang/test/Interpreter/code-undo.cpp @@ -1,10 +1,10 @@ -// RUN: cat %s | clang-repl | FileCheck %s +// RUN: cat %s | clang-repl -Xcc -I%S/Inputs | FileCheck %s extern "C" int printf(const char *, ...); int x1 = 0; int x2 = 42; %undo int x2 = 24; -auto r1 = printf("x1 = %d\n", x1); +auto r1 = printf("x1 = %d\n", x1); // CHECK: x1 = 0 auto r2 = printf("x2 = %d\n", x2); // CHECK-NEXT: x2 = 24 @@ -20,4 +20,29 @@ auto r4 = bar(); %undo auto r5 = bar(); +#include <cstdio> + +#include "dynamic-header.h" +auto r6 = printf("getDynamicValue() = %d\n", getDynamicValue()); +%undo +%undo +// CHECK-NEXT: getDynamicValue() = 100 +{ + FILE *f = fopen("%S/Inputs/dynamic-header.h", "w"); + if (f) { + fprintf(f, "#ifndef DYNAMIC_HEADER_H\n"); + fprintf(f, "#define DYNAMIC_HEADER_H\n"); + fprintf(f, "inline int getDynamicValue() { return 200; }\n"); + fprintf(f, "#endif\n"); + fclose(f); + } +} +#ifndef MACRO_FROM_HEADER +auto r7 = printf("MACRO_FROM_HEADER is unloaded\n"); +// CHECK-NEXT: MACRO_FROM_HEADER is unloaded +#endif +#include "dynamic-header.h" +auto r8 = printf("getDynamicValue() = %d\n", getDynamicValue()); +// CHECK-NEXT: getDynamicValue() = 200 + %quit _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
