[clang] [llvm] [Clang-Repl] Introduce Error Recovery for Clang-Repl. (PR #223305)

via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 1 01:34:24 PDT 2026


https://github.com/SahilPatidar updated https://github.com/llvm/llvm-project/pull/223305

>From 140cbe5c564e4e9e4645ddda72f6c9fa8b65eddc Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 26 Jul 2026 12:29:25 +0530
Subject: [PATCH 01/13] WIP: implement rollback (Error-Recovery)  mechanism for
 clang-repl

---
 clang/include/clang/AST/ASTContext.h          |   22 +-
 clang/include/clang/AST/Decl.h                |    3 +
 clang/include/clang/AST/DeclBase.h            |    6 +
 .../include/clang/AST/DeclContextInternals.h  |    3 +
 .../include/clang/Interpreter/ErrorRecovery.h |  300 ++++
 .../Interpreter/PartialTranslationUnit.h      |    3 +
 clang/include/clang/Sema/Sema.h               |    2 +
 clang/lib/AST/ASTContext.cpp                  |    4 +
 clang/lib/AST/DeclBase.cpp                    |    3 +
 .../lib/Interpreter/ASTContextStateStash.cpp  | 1362 +++++++++++++++++
 clang/lib/Interpreter/CMakeLists.txt          |    2 +
 clang/lib/Interpreter/IncrementalParser.cpp   |    2 +
 clang/lib/Interpreter/IncrementalParser.h     |    3 +
 clang/lib/Interpreter/Interpreter.cpp         |   37 +
 clang/lib/Interpreter/SemaStateStash.cpp      |  882 +++++++++++
 llvm/include/llvm/Support/Allocator.h         |   80 +-
 16 files changed, 2710 insertions(+), 4 deletions(-)
 create mode 100644 clang/include/clang/Interpreter/ErrorRecovery.h
 create mode 100644 clang/lib/Interpreter/ASTContextStateStash.cpp
 create mode 100644 clang/lib/Interpreter/SemaStateStash.cpp

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 72e6bcf362aeb..01f83b4d85113 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -85,6 +85,7 @@ template <> struct DenseMapInfo<ScalableVecTyKey> {
 namespace clang {
 
 class APValue;
+class ASTContextStateStash;
 class ASTMutationListener;
 class ASTRecordLayout;
 class AtomicExpr;
@@ -234,12 +235,22 @@ struct QualTypeBoolInfo {
   }
 };
 
+struct TypeForDeclMutation {
+  TypeDecl *Decl;
+  const Type *OldValue;
+};
+
 /// Holds long-lived AST nodes (such as types and decls) that can be
 /// referred to throughout the semantic analysis of a file.
 class ASTContext : public RefCountedBase<ASTContext> {
   friend class NestedNameSpecifier;
 
   mutable SmallVector<Type *, 0> Types;
+
+  bool IncrementalErrorRecoveryMode = true;
+  mutable SmallVector<TypeForDeclMutation> PendingTypeForDeclMutations;
+  mutable llvm::DenseSet<const DeclContext *> PendingDCMutations;
+
   mutable llvm::FoldingSet<ExtQuals> ExtQualNodes;
   mutable llvm::UniquingSet<ComplexType> ComplexTypes;
   mutable llvm::UniquingSet<PointerType> PointerTypes{GeneralTypesLog2InitSize};
@@ -605,13 +616,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
   using TemplateOrSpecializationInfo =
       llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *>;
 
+  bool isIncrementalErrorRecoveryMode() const {
+    return IncrementalErrorRecoveryMode;
+  }
+
 private:
   friend class ASTDeclReader;
   friend class ASTReader;
   friend class ASTWriter;
   template <class> friend class serialization::AbstractTypeReader;
   friend class CXXRecordDecl;
-  friend class IncrementalParser;
+  // friend class IncrementalParser;
+  friend class ASTContextStateStash;
 
   /// A mapping to contain the template or declaration that
   /// a variable declaration describes or was instantiated from,
@@ -1359,6 +1375,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
     TUDecl = NewTUDecl;
   }
 
+  void setTranslationUnitDecl(TranslationUnitDecl *NewTUDecl) {
+    TUDecl = NewTUDecl;
+  }
+
   ExternCContextDecl *getExternCContextDecl() const;
 
 #define BuiltinTemplate(BTName) BuiltinTemplateDecl *get##BTName##Decl() const;
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index c64dc8c02d40e..d5b7f6240d134 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -3644,9 +3644,12 @@ class IndirectFieldDecl : public ValueDecl,
   static bool classofKind(Kind K) { return K == IndirectField; }
 };
 
+class ASTContextStateStash;
+
 /// Represents a declaration of a type.
 class TypeDecl : public NamedDecl {
   friend class ASTContext;
+  friend class ASTContextStateStash;
   friend class ASTReader;
 
   /// This indicates the Type object that represents
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 8ea2533825dd9..39082a8973b2b 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -76,6 +76,8 @@ enum AvailabilityResult {
   AR_Unavailable
 };
 
+class ASTContextStateStash;
+
 /// Decl - This represents one declaration (or definition), e.g. a variable,
 /// typedef, function, struct, etc.
 ///
@@ -258,6 +260,7 @@ class alignas(8) Decl {
 
 private:
   friend class DeclContext;
+  friend class ASTContextStateStash;
 
   struct MultipleDC {
     DeclContext *SemanticDC;
@@ -1446,6 +1449,8 @@ enum class OMPDeclareReductionInitKind;
 enum class ObjCImplementationControl;
 enum class LinkageSpecLanguageIDs;
 
+class ASTContextStateStash;
+
 /// DeclContext - This is used only as base class of specific decl types that
 /// can act as declaration contexts. These decls are (only the top classes
 /// that directly derive from DeclContext are mentioned, not their subclasses):
@@ -1464,6 +1469,7 @@ enum class LinkageSpecLanguageIDs;
 ///   BlockDecl
 ///   CapturedDecl
 class DeclContext {
+  friend class ASTContextStateStash;
   /// For makeDeclVisibleInContextImpl
   friend class ASTDeclReader;
   /// For checking the new bits in the Serialization part.
diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h
index 87820b14d6188..f753e1e8f9eb4 100644
--- a/clang/include/clang/AST/DeclContextInternals.h
+++ b/clang/include/clang/AST/DeclContextInternals.h
@@ -301,10 +301,13 @@ class StoredDeclsList {
   }
 };
 
+class ASTContextStateStash;
+
 class StoredDeclsMap
     : public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> {
   friend class ASTContext; // walks the chain deleting these
   friend class DeclContext;
+  friend class ASTContextStateStash;
 
   llvm::PointerIntPair<StoredDeclsMap*, 1> Previous;
 public:
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
new file mode 100644
index 0000000000000..6d5605a9624e6
--- /dev/null
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -0,0 +1,300 @@
+//===--- ErrorRecovery.h - Errory Recovery Impl --------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
+#define LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
+
+#include "clang/Sema/Sema.h"
+
+namespace clang {
+class ASTContext;
+class Sema;
+
+struct SemaStashCheckPoint {
+  /// Sema::*
+  llvm::SlabCheckPoint SemaBumpSlabCP;
+  size_t CachedFunctionScopeSize = 0;
+  size_t FunctionScopesSize = 0;
+  size_t Ident_superSize = 0;
+  size_t PragmaClangBSSSectionSize = 0;
+  size_t PragmaClangDataSectionSize = 0;
+  size_t PragmaClangRodataSectionSize = 0;
+  size_t PragmaClangRelroSectionSize = 0;
+  size_t PragmaClangTextSectionSize = 0;
+  size_t VtorDispStackSize = 0;
+  size_t AlignPackStackSize = 0;
+  size_t AlignPackIncludeStackSize = 0;
+  size_t DataSegStackSize = 0;
+  size_t BSSSegStackSize = 0;
+  size_t ConstSegStackSize = 0;
+  size_t CodeSegStackSize = 0;
+  size_t StrictGuardStackCheckStackSize = 0;
+  size_t FpPragmaStackSize = 0;
+  size_t FunctionToSectionMapSize = 0;
+  size_t PragmaAttributeStackSize = 0;
+  size_t MSFunctionNoBuiltinsSize = 0;
+  size_t PendingExportedNamesSize = 0;
+  size_t TypoCorrectedFunctionDefinitionsSize = 0;
+  size_t FlagBitsCacheSize = 0;
+  size_t AssignEnumCacheSize = 0;
+  size_t WeakUndeclaredIdentifiersSize = 0;
+  size_t ExtnameUndeclaredIdentifiersSize = 0;
+  size_t UnusedLocalTypedefNameCandidatesSize = 0;
+  Sema::UnusedFileScopedDeclsType::iterator UnusedFileScopedDeclsSize;
+  Sema::TentativeDefinitionsType::iterator TentativeDefinitionsSize;
+  size_t ExternalDeclarationsSize = 0;
+  size_t ParsingInitForAutoVarsSize = 0;
+  size_t DeclsToCheckForDeferredDiagsSize = 0;
+  size_t ShadowingDeclsSize = 0;
+  size_t WeakTopLevelDeclSize = 0;
+  Sema::ExtVectorDeclsType::iterator ExtVectorDeclsSize;
+  size_t VTableUsesSize = 0;
+  size_t VTablesUsedSize = 0;
+  size_t DelayedDllExportClassesSize = 0;
+  size_t DelayedDllExportMemberFunctionsSize = 0;
+  size_t InventedParameterInfosSize = 0;
+  size_t FieldCollectorSize = 0;
+  size_t UnusedPrivateFieldsSize = 0;
+  size_t PureVirtualClassDiagSetSize = 0;
+  Sema::DelegatingCtorDeclsType::iterator DelegatingCtorDeclsSize;
+  size_t StdNamespaceSize = 0;
+  size_t UnparsedDefaultArgLocsSize = 0;
+  size_t UndefinedButUsedSize = 0;
+  size_t SpecialMembersBeingDeclaredSize = 0;
+  size_t DelayedOverridingExceptionSpecChecksSize = 0;
+  size_t DelayedEquivalentExceptionSpecChecksSize = 0;
+  size_t MaybeODRUseExprsSize = 0;
+  size_t RefsMinusAssignmentsSize = 0;
+  size_t ExprCleanupObjectsSize = 0;
+  size_t ExprEvalContextsSize = 0;
+  size_t FailedImmediateInvocationsSize = 0;
+  size_t ImplicitlyRetainedSelfLocsSize = 0;
+  size_t DeleteExprsSize = 0;
+  size_t CurrentParameterCopyTypesSize = 0;
+  size_t AggregateDeductionCandidatesSize = 0;
+  size_t TypoCorrectionFailuresSize = 0;
+  size_t SpecialMemberCacheSize = 0;
+  size_t ModuleScopesSize = 0;
+  size_t DeferredExportedNamespacesSize = 0;
+  size_t PendingInlineFuncDeclsSize = 0;
+  size_t CurrentSEHFinallySize = 0;
+  size_t CurrentDeferSize = 0;
+  size_t LateParsedTemplateMapSize = 0;
+  size_t SuppressedDiagnosticsSize = 0;
+  size_t CurrentInstantiationScopeSize = 0;
+  size_t UnparsedDefaultArgInstantiationsSize = 0;
+  size_t CodeSynthesisContextsSize = 0;
+  size_t InstantiatingSpecializationsSize = 0;
+  size_t InstantiatedNonDependentTypesSize = 0;
+  size_t CodeSynthesisContextLookupModulesSize = 0;
+  size_t LookupModulesCacheSize = 0;
+  size_t VisibleNamespaceCacheSize = 0;
+  size_t TemplateInstCallbacksSize = 0;
+  size_t PendingInstantiationsSize = 0;
+  size_t LateParsedInstantiationsSize = 0;
+  size_t SavedVTableUsesSize = 0;
+  size_t SavedPendingInstantiationsSize = 0;
+  size_t PendingLocalImplicitInstantiationsSize = 0;
+  size_t UnsubstitutedConstraintSatisfactionCacheSize = 0;
+  size_t SubsumptionCacheSize = 0;
+  size_t NormalizationCacheSize = 0;
+  size_t SatisfactionCacheSize = 0;
+  size_t SatisfactionStackSize = 0;
+//   size_t NullabilityMapSize = 0;
+  size_t DeclsWithEffectsToVerifySize = 0;
+  //   size_t AllEffectsToVerifySize = 0;
+};
+
+/// Stashes and restores persistent Sema state around an incremental parse.
+///
+/// Usage:
+///   SemaStashCheckPoint CP;
+///   SemaStateStash Stash(S);
+///   Stash.stash(CP);
+///   // ... parse ...
+///   if (failed)
+///     Stash.restore(CP, ASTSlabCP);
+class SemaStateStash {
+  Sema &S;
+
+  /// Holds full copies of PragmaStack, PragmaClangSection, FileNullabilityMap,
+  /// etc.
+  //   struct PragmaSnapshot;
+  //   std::unique_ptr<PragmaSnapshot> Pragmas;
+
+public:
+  explicit SemaStateStash(Sema &S) : S(S) {}
+  //   ~SemaStateStash();
+
+  //   SemaStateStash(const SemaStateStash &) = delete;
+  //   SemaStateStash &operator=(const SemaStateStash &) = delete;
+
+  void stash(SemaStashCheckPoint &CP);
+  void restore(SemaStashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
+};
+
+struct StashCheckPoint {
+  // Types vector
+  size_t TypesSize = 0;
+
+  // FoldingSets — store count of nodes
+  size_t ExtQualNodesSize = 0;
+  size_t ComplexTypesSize = 0;
+  size_t PointerTypesSize = 0;
+  size_t AdjustedTypesSize = 0;
+  size_t BlockPointerTypesSize = 0;
+  size_t LValueReferenceTypesSize = 0;
+  size_t RValueReferenceTypesSize = 0;
+  size_t MemberPointerTypesSize = 0;
+
+  size_t ConstantArrayTypesSize = 0;
+  size_t IncompleteArrayTypesSize = 0;
+  size_t VariableArrayTypesSize = 0;
+
+  size_t DependentSizedArrayTypesSize = 0;
+  size_t DependentSizedExtVectorTypesSize = 0;
+  size_t DependentAddressSpaceTypesSize = 0;
+  size_t VectorTypesSize = 0;
+  size_t DependentVectorTypesSize = 0;
+  size_t MatrixTypesSize = 0;
+  size_t DependentSizedMatrixTypesSize = 0;
+  size_t FunctionNoProtoTypesSize = 0;
+  size_t FunctionProtoTypesSize = 0;
+  size_t DependentTypeOfExprTypesSize = 0;
+  size_t DependentDecltypeTypesSize = 0;
+
+  size_t DependentPackIndexingTypesSize = 0;
+
+  size_t TemplateTypeParmTypesSize = 0;
+  size_t ObjCTypeParamTypesSize = 0;
+  size_t SubstTemplateTypeParmTypesSize = 0;
+  size_t SubstTemplateTypeParmPackTypesSize = 0;
+  size_t SubstBuiltinTemplatePackTypesSize = 0;
+
+  size_t TemplateSpecializationTypesSize = 0;
+  size_t ParenTypesSize = 0;
+  size_t TagTypesSize = 0;
+  size_t UnresolvedUsingTypesSize = 0;
+  size_t UsingTypesSize = 0;
+  size_t TypedefTypesSize = 0;
+  size_t DependentNameTypesSize = 0;
+  size_t PackExpansionTypesSize = 0;
+  size_t ObjCObjectTypesSize = 0;
+  size_t ObjCObjectPointerTypesSize = 0;
+  size_t UnaryTransformTypesSize = 0;
+
+  size_t AutoTypesSize = 0;
+  size_t DeducedTemplateSpecializationTypesSize = 0;
+  size_t AtomicTypesSize = 0;
+  size_t AttributedTypesSize = 0;
+  size_t PipeTypesSize = 0;
+  size_t BitIntTypesSize = 0;
+  size_t DependentBitIntTypesSize = 0;
+  size_t BTFTagAttributedTypesSize = 0;
+  size_t HLSLAttributedResourceTypesSize = 0;
+  size_t HLSLInlineSpirvTypesSize = 0;
+
+  size_t CountAttributedTypesSize = 0;
+
+  size_t QualifiedTemplateNamesSize = 0;
+  size_t DependentTemplateNamesSize = 0;
+  size_t SubstTemplateTemplateParmsSize = 0;
+  size_t SubstTemplateTemplateParmPacksSize = 0;
+  size_t DeducedTemplatesSize = 0;
+
+  size_t ArrayParameterTypesSize = 0;
+
+  size_t PredefinedSugarTypesSize = 0;
+
+  size_t NamespaceAndPrefixStoragesSize = 0;
+
+  size_t ASTRecordLayoutsSize = 0;
+
+  size_t MemoizedTypeInfoSize = 0;
+
+  size_t MemoizedUnadjustedAlignSize = 0;
+
+  size_t KeyFunctionsSize = 0;
+
+  size_t BlockVarCopyInitsSize = 0;
+
+  size_t MSGuidDeclsSize = 0;
+
+  size_t UnnamedGlobalConstantDeclsSize = 0;
+
+  size_t TemplateParamObjectDeclsSize = 0;
+
+  size_t StringLiteralCacheSize = 0;
+
+  size_t DestroyingOperatorDeletesSize = 0;
+  size_t TypeAwareOperatorNewAndDeletesSize = 0;
+
+  size_t OperatorDeletesForVirtualDtorSize = 0;
+
+  size_t GlobalOperatorDeletesForVirtualDtorSize = 0;
+
+  size_t ArrayOperatorDeletesForVirtualDtorSize = 0;
+  size_t GlobalArrayOperatorDeletesForVirtualDtorSize = 0;
+
+  size_t RequireVectorDeletingDtorSize = 0;
+
+  size_t MergedDeclsSize = 0;
+  size_t MergedDefModulesSize = 0;
+
+  size_t ModuleInitializersSize = 0;
+  size_t PrimaryModuleNameMapSize = 0;
+  size_t SameModuleLookupSetSize = 0;
+
+  size_t ScalableVecTyMapSize = 0;
+  size_t LambdaCastPathsSize = 0;
+  size_t DeclRawCommentsSize = 0;
+  size_t RedeclChainCommentsSize = 0;
+  size_t CommentlessRedeclChainsSize = 0;
+  size_t ParsedCommentsSize = 0;
+  size_t RelocatableClassesSize = 0;
+  size_t ParamIndicesSize = 0;
+  size_t MangleNumbersSize = 0;
+  size_t StaticLocalNumbersSize = 0;
+  size_t TemplateOrInstantiationSize = 0;
+
+  size_t InstantiatedFromUsingDeclSize = 0;
+  size_t InstantiatedFromUsingEnumDeclSize = 0;
+
+  size_t InstantiatedFromUsingShadowDeclSize = 0;
+
+  size_t InstantiatedFromUnnamedFieldDeclSize = 0;
+  size_t OverriddenMethodsSize = 0;
+  size_t MangleNumberingContextsSize = 0;
+  size_t ExtraMangleNumberingContextsSize = 0;
+
+  size_t TraversalScopeSize = 0;
+  llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
+//    = llvm::PointerIntPair<StoredDeclsMap *, 1>(nullptr, 0);
+};
+
+class ASTContextStateStash {
+private:
+  ASTContext &Ctx;
+
+public:
+  explicit ASTContextStateStash(ASTContext &Ctx) : Ctx(Ctx) {}
+
+  ASTContextStateStash(const ASTContextStateStash &) = delete;
+  ASTContextStateStash &operator=(const ASTContextStateStash &) = delete;
+
+  void stash(StashCheckPoint &CP);
+  void restore(StashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
+  void commit();
+};
+
+} // end namespace clang
+#endif // LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
diff --git a/clang/include/clang/Interpreter/PartialTranslationUnit.h b/clang/include/clang/Interpreter/PartialTranslationUnit.h
index c878e139fe70d..843f20f82e3bc 100644
--- a/clang/include/clang/Interpreter/PartialTranslationUnit.h
+++ b/clang/include/clang/Interpreter/PartialTranslationUnit.h
@@ -14,6 +14,8 @@
 #ifndef LLVM_CLANG_INTERPRETER_PARTIALTRANSLATIONUNIT_H
 #define LLVM_CLANG_INTERPRETER_PARTIALTRANSLATIONUNIT_H
 
+#include "llvm/Support/Allocator.h"
+
 #include <memory>
 
 namespace llvm {
@@ -28,6 +30,7 @@ class TranslationUnitDecl;
 /// incremental inputs.
 struct PartialTranslationUnit {
   TranslationUnitDecl *TUPart = nullptr;
+  llvm::SlabCheckPoint SlabCheckPoint;
 
   /// The llvm IR produced for the input.
   std::unique_ptr<llvm::Module> TheModule;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e33073c652dba..feb1b9eea5c7b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -186,6 +186,7 @@ class SemaSystemZ;
 class SemaWasm;
 class SemaX86;
 class StandardConversionSequence;
+class SemaStateStash;
 class TemplateArgument;
 class TemplateArgumentLoc;
 class TemplateInstantiationCallback;
@@ -1587,6 +1588,7 @@ class Sema final : public SemaBase {
   friend class ASTReader;
   friend class ASTDeclReader;
   friend class ASTWriter;
+  friend class SemaStateStash;
 
 private:
   std::optional<std::unique_ptr<DarwinSDKInfo>> CachedDarwinSDKInfo;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a361b07e201b3..e850e9a0b61d1 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5621,6 +5621,10 @@ QualType ASTContext::getTagType(ElaboratedTypeKeyword Keyword,
                            /*Qualifier=*/std::nullopt, NonInjectedTD,
                            /*OwnsTag=*/false, IsInjected, CanonicalType,
                            /*WithFoldingSetNode=*/false);
+
+    if (IncrementalErrorRecoveryMode)
+      PendingTypeForDeclMutations.push_back({const_cast<TagDecl *>(TD), TD->TypeForDecl});
+
     TD->TypeForDecl = T;
     return QualType(T, 0);
   }
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 548643cea2cce..4bc4b8ca2acdc 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -2211,6 +2211,9 @@ void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D, bool Internal) {
   }
 
   DeclNameEntries.addOrReplaceDecl(D);
+
+  if (getParentASTContext().isIncrementalErrorRecoveryMode())
+    getParentASTContext().PendingDCMutations.insert(this);
 }
 
 UsingDirectiveDecl *DeclContext::udir_iterator::operator*() const {
diff --git a/clang/lib/Interpreter/ASTContextStateStash.cpp b/clang/lib/Interpreter/ASTContextStateStash.cpp
new file mode 100644
index 0000000000000..18ad727ddc5ef
--- /dev/null
+++ b/clang/lib/Interpreter/ASTContextStateStash.cpp
@@ -0,0 +1,1362 @@
+//===--- ASTContextStateStash.cpp - ASTContext persistent state stash/restore
+//----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Interpreter/ErrorRecovery.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclContextInternals.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Type.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang {
+
+/// Erases nodes from a FoldingSet based on a predicate.
+template <typename EntryType, typename PredT>
+static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
+  SmallVector<EntryType *, 16> ToRemove;
+
+  for (auto &N : FS)
+    if (Pred(N))
+      ToRemove.push_back(&N);
+
+  for (auto *N : ToRemove)
+    FS.RemoveNode(N);
+}
+
+template <typename EntryType, typename PredT>
+static void
+eraseFoldingSetIf(llvm::ContextualFoldingSet<EntryType, ASTContext &> &FS,
+                  PredT &&Pred) {
+  SmallVector<EntryType *, 16> ToRemove;
+
+  for (auto &N : FS)
+    if (Pred(N))
+      ToRemove.push_back(&N);
+
+  for (auto *N : ToRemove)
+    FS.RemoveNode(N);
+}
+
+/// Erase from DenseMap based on predicate
+template <typename KeyT, typename ValueT, typename PredT>
+static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
+  SmallVector<KeyT, 16> ToRemove;
+
+  for (auto &KV : Map)
+    if (Pred(KV))
+      ToRemove.push_back(KV.getFirst());
+
+  for (auto &Key : ToRemove)
+    Map.erase(Key);
+}
+
+/// Erase from DenseMap based on predicate
+template <typename ValueT, typename PredT>
+static void eraseDenseSetIf(llvm::DenseSet<ValueT> &Set, PredT &&Pred) {
+  SmallVector<ValueT, 16> ToRemove;
+
+  for (auto &V : Set)
+    if (Pred(V))
+      ToRemove.push_back(V);
+
+  for (auto &Key : ToRemove)
+    Set.erase(Key);
+}
+
+void ASTContextStateStash::stash(StashCheckPoint &CP) {
+  CP.TypesSize = Ctx.Types.size();
+  CP.ExtQualNodesSize = Ctx.ExtQualNodes.size();
+  CP.ComplexTypesSize = Ctx.ComplexTypes.size();
+  CP.PointerTypesSize = Ctx.PointerTypes.size();
+  CP.AdjustedTypesSize = Ctx.AdjustedTypes.size();
+  CP.BlockPointerTypesSize = Ctx.BlockPointerTypes.size();
+  CP.LValueReferenceTypesSize = Ctx.LValueReferenceTypes.size();
+  CP.RValueReferenceTypesSize = Ctx.RValueReferenceTypes.size();
+  CP.MemberPointerTypesSize = Ctx.MemberPointerTypes.size();
+
+  CP.ConstantArrayTypesSize = Ctx.ConstantArrayTypes.size();
+  CP.IncompleteArrayTypesSize = Ctx.IncompleteArrayTypes.size();
+  CP.VariableArrayTypesSize = Ctx.VariableArrayTypes.size();
+
+  CP.DependentSizedArrayTypesSize = Ctx.DependentSizedArrayTypes.size();
+  CP.DependentSizedExtVectorTypesSize = Ctx.DependentSizedExtVectorTypes.size();
+  CP.DependentAddressSpaceTypesSize = Ctx.DependentAddressSpaceTypes.size();
+  CP.VectorTypesSize = Ctx.VectorTypes.size();
+  CP.DependentVectorTypesSize = Ctx.DependentVectorTypes.size();
+  CP.MatrixTypesSize = Ctx.MatrixTypes.size();
+  CP.DependentSizedMatrixTypesSize = Ctx.DependentSizedMatrixTypes.size();
+  CP.FunctionNoProtoTypesSize = Ctx.FunctionNoProtoTypes.size();
+  CP.FunctionProtoTypesSize = Ctx.FunctionProtoTypes.size();
+  CP.DependentTypeOfExprTypesSize = Ctx.DependentTypeOfExprTypes.size();
+  CP.DependentDecltypeTypesSize = Ctx.DependentDecltypeTypes.size();
+
+  CP.DependentPackIndexingTypesSize = Ctx.DependentPackIndexingTypes.size();
+
+  CP.TemplateTypeParmTypesSize = Ctx.TemplateTypeParmTypes.size();
+  CP.ObjCTypeParamTypesSize = Ctx.ObjCTypeParamTypes.size();
+  CP.SubstTemplateTypeParmTypesSize = Ctx.SubstTemplateTypeParmTypes.size();
+  CP.SubstTemplateTypeParmPackTypesSize =
+      Ctx.SubstTemplateTypeParmPackTypes.size();
+  CP.SubstBuiltinTemplatePackTypesSize =
+      Ctx.SubstBuiltinTemplatePackTypes.size();
+
+  CP.TemplateSpecializationTypesSize = Ctx.TemplateSpecializationTypes.size();
+  CP.ParenTypesSize = Ctx.ParenTypes.size();
+  CP.TagTypesSize = Ctx.TagTypes.size();
+  CP.UnresolvedUsingTypesSize = Ctx.UnresolvedUsingTypes.size();
+  CP.UsingTypesSize = Ctx.UsingTypes.size();
+  CP.TypedefTypesSize = Ctx.TypedefTypes.size();
+  CP.DependentNameTypesSize = Ctx.DependentNameTypes.size();
+  CP.PackExpansionTypesSize = Ctx.PackExpansionTypes.size();
+  CP.ObjCObjectTypesSize = Ctx.ObjCObjectTypes.size();
+  CP.ObjCObjectPointerTypesSize = Ctx.ObjCObjectPointerTypes.size();
+  CP.UnaryTransformTypesSize = Ctx.UnaryTransformTypes.size();
+
+  CP.AutoTypesSize = Ctx.AutoTypes.size();
+  CP.DeducedTemplateSpecializationTypesSize =
+      Ctx.DeducedTemplateSpecializationTypes.size();
+  CP.AtomicTypesSize = Ctx.AtomicTypes.size();
+  CP.AttributedTypesSize = Ctx.AttributedTypes.size();
+  CP.PipeTypesSize = Ctx.PipeTypes.size();
+  CP.BitIntTypesSize = Ctx.BitIntTypes.size();
+  CP.DependentBitIntTypesSize = Ctx.DependentBitIntTypes.size();
+  CP.BTFTagAttributedTypesSize = Ctx.BTFTagAttributedTypes.size();
+  CP.HLSLAttributedResourceTypesSize = Ctx.HLSLAttributedResourceTypes.size();
+  CP.HLSLInlineSpirvTypesSize = Ctx.HLSLInlineSpirvTypes.size();
+
+  CP.CountAttributedTypesSize = Ctx.CountAttributedTypes.size();
+
+  CP.QualifiedTemplateNamesSize = Ctx.QualifiedTemplateNames.size();
+  CP.DependentTemplateNamesSize = Ctx.DependentTemplateNames.size();
+  CP.SubstTemplateTemplateParmsSize = Ctx.SubstTemplateTemplateParms.size();
+  CP.SubstTemplateTemplateParmPacksSize =
+      Ctx.SubstTemplateTemplateParmPacks.size();
+  CP.DeducedTemplatesSize = Ctx.DeducedTemplates.size();
+
+  CP.ArrayParameterTypesSize = Ctx.ArrayParameterTypes.size();
+
+  CP.PredefinedSugarTypesSize = Ctx.PredefinedSugarTypes.size();
+
+  CP.NamespaceAndPrefixStoragesSize = Ctx.NamespaceAndPrefixStorages.size();
+
+  CP.ASTRecordLayoutsSize = Ctx.ASTRecordLayouts.size();
+
+  CP.MemoizedTypeInfoSize = Ctx.MemoizedTypeInfo.size();
+
+  CP.MemoizedUnadjustedAlignSize = Ctx.MemoizedUnadjustedAlign.size();
+
+  CP.KeyFunctionsSize = Ctx.KeyFunctions.size();
+
+  CP.BlockVarCopyInitsSize = Ctx.BlockVarCopyInits.size();
+
+  CP.MSGuidDeclsSize = Ctx.MSGuidDecls.size();
+
+  CP.UnnamedGlobalConstantDeclsSize = Ctx.UnnamedGlobalConstantDecls.size();
+
+  CP.TemplateParamObjectDeclsSize = Ctx.TemplateParamObjectDecls.size();
+
+  CP.StringLiteralCacheSize = Ctx.StringLiteralCache.size();
+
+  CP.DestroyingOperatorDeletesSize = Ctx.DestroyingOperatorDeletes.size();
+  CP.TypeAwareOperatorNewAndDeletesSize =
+      Ctx.TypeAwareOperatorNewAndDeletes.size();
+
+  CP.OperatorDeletesForVirtualDtorSize =
+      Ctx.OperatorDeletesForVirtualDtor.size();
+
+  CP.GlobalOperatorDeletesForVirtualDtorSize =
+      Ctx.GlobalOperatorDeletesForVirtualDtor.size();
+
+  CP.ArrayOperatorDeletesForVirtualDtorSize =
+      Ctx.ArrayOperatorDeletesForVirtualDtor.size();
+  CP.GlobalArrayOperatorDeletesForVirtualDtorSize =
+      Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size();
+
+  CP.RequireVectorDeletingDtorSize = Ctx.RequireVectorDeletingDtor.size();
+
+  CP.MergedDeclsSize = Ctx.MergedDecls.size();
+  CP.MergedDefModulesSize = Ctx.MergedDefModules.size();
+
+  CP.ModuleInitializersSize = Ctx.ModuleInitializers.size();
+  CP.PrimaryModuleNameMapSize = Ctx.PrimaryModuleNameMap.size();
+  CP.SameModuleLookupSetSize = Ctx.SameModuleLookupSet.size();
+
+  CP.ScalableVecTyMapSize = Ctx.ScalableVecTyMap.size();
+  CP.LambdaCastPathsSize = Ctx.LambdaCastPaths.size();
+  CP.DeclRawCommentsSize = Ctx.DeclRawComments.size();
+  CP.RedeclChainCommentsSize = Ctx.RedeclChainComments.size();
+  CP.CommentlessRedeclChainsSize = Ctx.CommentlessRedeclChains.size();
+  CP.ParsedCommentsSize = Ctx.ParsedComments.size();
+  CP.RelocatableClassesSize = Ctx.RelocatableClasses.size();
+  CP.ParamIndicesSize = Ctx.ParamIndices.size();
+  CP.MangleNumbersSize = Ctx.MangleNumbers.size();
+  CP.StaticLocalNumbersSize = Ctx.StaticLocalNumbers.size();
+  CP.TemplateOrInstantiationSize = Ctx.TemplateOrInstantiation.size();
+
+  CP.InstantiatedFromUsingDeclSize = Ctx.InstantiatedFromUsingDecl.size();
+  CP.InstantiatedFromUsingEnumDeclSize =
+      Ctx.InstantiatedFromUsingEnumDecl.size();
+
+  CP.InstantiatedFromUsingShadowDeclSize =
+      Ctx.InstantiatedFromUsingShadowDecl.size();
+
+  CP.InstantiatedFromUnnamedFieldDeclSize =
+      Ctx.InstantiatedFromUnnamedFieldDecl.size();
+  CP.OverriddenMethodsSize = Ctx.OverriddenMethods.size();
+  CP.MangleNumberingContextsSize = Ctx.MangleNumberingContexts.size();
+  CP.ExtraMangleNumberingContextsSize = Ctx.ExtraMangleNumberingContexts.size();
+  CP.TraversalScopeSize = Ctx.TraversalScope.size();
+  CP.LastSDM = Ctx.LastSDM;
+}
+
+void ASTContextStateStash::restore(StashCheckPoint &CP,
+                                   llvm::SlabCheckPoint SlabCP) {
+
+  if (Ctx.TemplateTypeParmTypes.size() != CP.TemplateTypeParmTypesSize) {
+    llvm::dbgs() << "Ctx.TemplateTypeParmTypes.size() != "
+                    "CP.TemplateTypeParmTypesSize\n";
+    eraseFoldingSetIf(Ctx.TemplateTypeParmTypes,
+                      [&](TemplateTypeParmType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.TemplateTypeParmTypes.size() == CP.TemplateTypeParmTypesSize);
+  }
+
+  if (Ctx.TagTypes.size() != CP.TagTypesSize) {
+    llvm::dbgs() << "Ctx.TagTypes.size() != CP.TagTypesSize\n";
+    eraseFoldingSetIf(Ctx.TagTypes,
+                      [&](TagTypeFoldingSetPlaceholder &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+
+    assert(Ctx.TagTypes.size() == CP.TagTypesSize);
+  }
+
+  if (Ctx.ExtQualNodes.size() != CP.ExtQualNodesSize) {
+    llvm::dbgs() << "Ctx.ExtQualNodes.size() != CP.ExtQualNodesSize\n";
+    eraseFoldingSetIf(Ctx.ExtQualNodes, [&](ExtQuals &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.ExtQualNodes.size() == CP.ExtQualNodesSize);
+  }
+
+  if (Ctx.ComplexTypes.size() != CP.ComplexTypesSize) {
+    llvm::dbgs() << "Ctx.ComplexTypes.size() != CP.ComplexTypesSize\n";
+    eraseFoldingSetIf(Ctx.ComplexTypes, [&](ComplexType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.ComplexTypes.size() == CP.ComplexTypesSize);
+  }
+
+  if (Ctx.PointerTypes.size() != CP.PointerTypesSize) {
+    llvm::dbgs() << "Ctx.PointerTypes.size() != CP.PointerTypesSize\n";
+    eraseFoldingSetIf(Ctx.PointerTypes, [&](PointerType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.PointerTypes.size() == CP.PointerTypesSize);
+  }
+
+  if (Ctx.AdjustedTypes.size() != CP.AdjustedTypesSize) {
+    llvm::dbgs() << "Ctx.AdjustedTypes.size() != CP.AdjustedTypesSize\n";
+    eraseFoldingSetIf(Ctx.AdjustedTypes, [&](AdjustedType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.AdjustedTypes.size() == CP.AdjustedTypesSize);
+  }
+
+  if (Ctx.BlockPointerTypes.size() != CP.BlockPointerTypesSize) {
+    llvm::dbgs()
+        << "Ctx.BlockPointerTypes.size() != CP.BlockPointerTypesSize\n";
+    eraseFoldingSetIf(Ctx.BlockPointerTypes,
+                      [&](BlockPointerType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.BlockPointerTypes.size() == CP.BlockPointerTypesSize);
+  }
+
+  if (Ctx.LValueReferenceTypes.size() != CP.LValueReferenceTypesSize) {
+    llvm::dbgs()
+        << "Ctx.LValueReferenceTypes.size() != CP.LValueReferenceTypesSize\n";
+    eraseFoldingSetIf(Ctx.LValueReferenceTypes,
+                      [&](LValueReferenceType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.LValueReferenceTypes.size() == CP.LValueReferenceTypesSize);
+  }
+
+  if (Ctx.RValueReferenceTypes.size() != CP.RValueReferenceTypesSize) {
+    llvm::dbgs()
+        << "Ctx.RValueReferenceTypes.size() != CP.RValueReferenceTypesSize\n";
+    eraseFoldingSetIf(Ctx.RValueReferenceTypes,
+                      [&](RValueReferenceType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.RValueReferenceTypes.size() == CP.RValueReferenceTypesSize);
+  }
+
+  if (Ctx.MemberPointerTypes.size() != CP.MemberPointerTypesSize) {
+    llvm::dbgs()
+        << "Ctx.MemberPointerTypes.size() != CP.MemberPointerTypesSize\n";
+    eraseFoldingSetIf(Ctx.MemberPointerTypes,
+                      [&](MemberPointerType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.MemberPointerTypes.size() == CP.MemberPointerTypesSize);
+  }
+
+  if (Ctx.ConstantArrayTypes.size() != CP.ConstantArrayTypesSize) {
+    llvm::dbgs()
+        << "Ctx.ConstantArrayTypes.size() != CP.ConstantArrayTypesSize\n";
+    eraseFoldingSetIf(Ctx.ConstantArrayTypes,
+                      [&](ConstantArrayType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.ConstantArrayTypes.size() == CP.ConstantArrayTypesSize);
+  }
+
+  if (Ctx.IncompleteArrayTypes.size() != CP.IncompleteArrayTypesSize) {
+    llvm::dbgs()
+        << "Ctx.IncompleteArrayTypes.size() != CP.IncompleteArrayTypesSize\n";
+    eraseFoldingSetIf(Ctx.IncompleteArrayTypes,
+                      [&](IncompleteArrayType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.IncompleteArrayTypes.size() == CP.IncompleteArrayTypesSize);
+  }
+
+  // mutable std::vector<VariableArrayType*> VariableArrayTypes;
+  if (Ctx.VariableArrayTypes.size() != CP.VariableArrayTypesSize) {
+    llvm::dbgs()
+        << "Ctx.VariableArrayTypes.size() != CP.VariableArrayTypesSize\n";
+    Ctx.VariableArrayTypes.resize(CP.VariableArrayTypesSize);
+    assert(Ctx.VariableArrayTypes.size() == CP.VariableArrayTypesSize);
+  }
+
+  if (Ctx.DependentSizedArrayTypes.size() != CP.DependentSizedArrayTypesSize) {
+    llvm::dbgs() << "Ctx.DependentSizedArrayTypes.size() != "
+                    "CP.DependentSizedArrayTypesSize\n";
+    eraseFoldingSetIf(Ctx.DependentSizedArrayTypes,
+                      [&](DependentSizedArrayType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentSizedArrayTypes.size() ==
+           CP.DependentSizedArrayTypesSize);
+  }
+
+  if (Ctx.DependentSizedExtVectorTypes.size() !=
+      CP.DependentSizedExtVectorTypesSize) {
+    llvm::dbgs() << "Ctx.DependentSizedExtVectorTypes.size() != "
+                    "CP.DependentSizedExtVectorTypesSize\n";
+    eraseFoldingSetIf(Ctx.DependentSizedExtVectorTypes,
+                      [&](DependentSizedExtVectorType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentSizedExtVectorTypes.size() ==
+           CP.DependentSizedExtVectorTypesSize);
+  }
+
+  if (Ctx.DependentAddressSpaceTypes.size() !=
+      CP.DependentAddressSpaceTypesSize) {
+    llvm::dbgs() << "Ctx.DependentAddressSpaceTypes.size() != "
+                    "CP.DependentAddressSpaceTypesSize\n";
+
+    eraseFoldingSetIf(Ctx.DependentAddressSpaceTypes,
+                      [&](DependentAddressSpaceType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentAddressSpaceTypes.size() ==
+           CP.DependentAddressSpaceTypesSize);
+  }
+
+  if (Ctx.VectorTypes.size() != CP.VectorTypesSize) {
+    llvm::dbgs() << "Ctx.VectorTypes.size() != CP.VectorTypesSize\n";
+    eraseFoldingSetIf(Ctx.VectorTypes, [&](VectorType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.VectorTypes.size() == CP.VectorTypesSize);
+  }
+
+  if (Ctx.DependentVectorTypes.size() != CP.DependentVectorTypesSize) {
+    llvm::dbgs()
+        << "Ctx.DependentVectorTypes.size() != CP.DependentVectorTypesSize\n";
+    eraseFoldingSetIf(Ctx.DependentVectorTypes,
+                      [&](DependentVectorType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentVectorTypes.size() == CP.DependentVectorTypesSize);
+  }
+
+  if (Ctx.MatrixTypes.size() != CP.MatrixTypesSize) {
+    llvm::dbgs() << "Ctx.MatrixTypes.size() != CP.MatrixTypesSize\n";
+    eraseFoldingSetIf(Ctx.MatrixTypes, [&](ConstantMatrixType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.MatrixTypes.size() == CP.MatrixTypesSize);
+  }
+
+  if (Ctx.DependentSizedMatrixTypes.size() !=
+      CP.DependentSizedMatrixTypesSize) {
+    llvm::dbgs() << "Ctx.DependentSizedMatrixTypes.size() != "
+                    "CP.DependentSizedMatrixTypesSize\n";
+    eraseFoldingSetIf(Ctx.DependentSizedMatrixTypes,
+                      [&](DependentSizedMatrixType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentSizedMatrixTypes.size() ==
+           CP.DependentSizedMatrixTypesSize);
+  }
+
+  if (Ctx.FunctionNoProtoTypes.size() != CP.FunctionNoProtoTypesSize) {
+    llvm::dbgs()
+        << "Ctx.FunctionNoProtoTypes.size() != CP.FunctionNoProtoTypesSize\n";
+    eraseFoldingSetIf(Ctx.FunctionNoProtoTypes,
+                      [&](FunctionNoProtoType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.FunctionNoProtoTypes.size() == CP.FunctionNoProtoTypesSize);
+  }
+
+  if (Ctx.FunctionProtoTypes.size() != CP.FunctionProtoTypesSize) {
+    llvm::dbgs()
+        << "Ctx.FunctionProtoTypes.size() != CP.FunctionProtoTypesSize\n";
+    eraseFoldingSetIf(Ctx.FunctionProtoTypes,
+                      [&](FunctionProtoType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.FunctionProtoTypes.size() == CP.FunctionProtoTypesSize);
+  }
+
+  if (Ctx.DependentTypeOfExprTypes.size() != CP.DependentTypeOfExprTypesSize) {
+    llvm::dbgs() << "Ctx.DependentTypeOfExprTypes.size() != "
+                    "CP.DependentTypeOfExprTypesSize\n";
+    eraseFoldingSetIf(Ctx.DependentTypeOfExprTypes,
+                      [&](DependentTypeOfExprType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentTypeOfExprTypes.size() ==
+           CP.DependentTypeOfExprTypesSize);
+  }
+
+  if (Ctx.DependentDecltypeTypes.size() != CP.DependentDecltypeTypesSize) {
+    llvm::dbgs() << "Ctx.DependentDecltypeTypes.size() != "
+                    "CP.DependentDecltypeTypesSize\n";
+    eraseFoldingSetIf(Ctx.DependentDecltypeTypes,
+                      [&](DependentDecltypeType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentDecltypeTypes.size() == CP.DependentDecltypeTypesSize);
+  }
+
+  if (Ctx.DependentPackIndexingTypes.size() !=
+      CP.DependentPackIndexingTypesSize) {
+    llvm::dbgs() << "Ctx.DependentPackIndexingTypes.size() != "
+                    "CP.DependentPackIndexingTypesSize\n";
+    eraseFoldingSetIf(Ctx.DependentPackIndexingTypes,
+                      [&](PackIndexingType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentPackIndexingTypes.size() ==
+           CP.DependentPackIndexingTypesSize);
+  }
+
+  if (Ctx.TemplateTypeParmTypes.size() != CP.TemplateTypeParmTypesSize) {
+    llvm::dbgs() << "Ctx.TemplateTypeParmTypes.size() != "
+                    "CP.TemplateTypeParmTypesSize\n";
+    eraseFoldingSetIf(Ctx.TemplateTypeParmTypes,
+                      [&](TemplateTypeParmType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.TemplateTypeParmTypes.size() == CP.TemplateTypeParmTypesSize);
+  }
+
+  // mutable llvm::FoldingSet<ObjCTypeParamType> ObjCTypeParamTypes;
+
+  if (Ctx.SubstTemplateTypeParmTypes.size() !=
+      CP.SubstTemplateTypeParmTypesSize) {
+    llvm::dbgs() << "Ctx.SubstTemplateTypeParmTypes.size() != "
+                    "CP.SubstTemplateTypeParmTypesSize\n";
+    eraseFoldingSetIf(Ctx.SubstTemplateTypeParmTypes,
+                      [&](SubstTemplateTypeParmType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.SubstTemplateTypeParmTypes.size() ==
+           CP.SubstTemplateTypeParmTypesSize);
+  }
+
+  if (Ctx.SubstTemplateTypeParmPackTypes.size() !=
+      CP.SubstTemplateTypeParmPackTypesSize) {
+    llvm::dbgs() << "Ctx.SubstTemplateTypeParmPackTypes.size() != "
+                    "CP.SubstTemplateTypeParmPackTypesSize\n";
+    eraseFoldingSetIf(Ctx.SubstTemplateTypeParmPackTypes,
+                      [&](SubstTemplateTypeParmPackType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.SubstTemplateTypeParmPackTypes.size() ==
+           CP.SubstTemplateTypeParmPackTypesSize);
+  }
+
+  if (Ctx.SubstBuiltinTemplatePackTypes.size() !=
+      CP.SubstBuiltinTemplatePackTypesSize) {
+    llvm::dbgs() << "Ctx.SubstBuiltinTemplatePackTypes.size() != "
+                    "CP.SubstBuiltinTemplatePackTypesSize\n";
+    eraseFoldingSetIf(Ctx.SubstBuiltinTemplatePackTypes,
+                      [&](SubstBuiltinTemplatePackType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.SubstBuiltinTemplatePackTypes.size() ==
+           CP.SubstBuiltinTemplatePackTypesSize);
+  }
+
+  if (Ctx.TemplateSpecializationTypes.size() !=
+      CP.TemplateSpecializationTypesSize) {
+    llvm::dbgs() << "Ctx.TemplateSpecializationTypes.size() != "
+                    "CP.TemplateSpecializationTypesSize\n";
+    eraseFoldingSetIf(Ctx.TemplateSpecializationTypes,
+                      [&](TemplateSpecializationType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.TemplateSpecializationTypes.size() ==
+           CP.TemplateSpecializationTypesSize);
+  }
+
+  if (Ctx.ParenTypes.size() != CP.ParenTypesSize) {
+    llvm::dbgs() << "Ctx.ParenTypes.size() != CP.ParenTypesSize\n";
+    eraseFoldingSetIf(Ctx.ParenTypes, [&](ParenType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.ParenTypes.size() == CP.ParenTypesSize);
+  }
+
+  if (Ctx.TagTypes.size() != CP.TagTypesSize) {
+    llvm::dbgs() << "Ctx.TagTypes.size() != CP.TagTypesSize\n";
+    eraseFoldingSetIf(Ctx.TagTypes,
+                      [&](TagTypeFoldingSetPlaceholder &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.TagTypes.size() == CP.TagTypesSize);
+  }
+
+  if (Ctx.UnresolvedUsingTypes.size() != CP.UnresolvedUsingTypesSize) {
+    llvm::dbgs()
+        << "Ctx.UnresolvedUsingTypes.size() != CP.UnresolvedUsingTypesSize\n";
+    eraseFoldingSetIf(
+        Ctx.UnresolvedUsingTypes,
+        [&](FoldingSetPlaceholder<UnresolvedUsingType> &Node) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+              static_cast<void *>(&Node), SlabCP);
+        });
+    assert(Ctx.UnresolvedUsingTypes.size() == CP.UnresolvedUsingTypesSize);
+  }
+
+  if (Ctx.UsingTypes.size() != CP.UsingTypesSize) {
+    llvm::dbgs() << "Ctx.UsingTypes.size() != CP.UsingTypesSize\n";
+    eraseFoldingSetIf(Ctx.UsingTypes, [&](UsingType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.UsingTypes.size() == CP.UsingTypesSize);
+  }
+
+  if (Ctx.TypedefTypes.size() != CP.TypedefTypesSize) {
+    llvm::dbgs() << "Ctx.TypedefTypes.size() != CP.TypedefTypesSize\n";
+    eraseFoldingSetIf(Ctx.TypedefTypes,
+                      [&](FoldingSetPlaceholder<TypedefType> &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.TypedefTypes.size() == CP.TypedefTypesSize);
+  }
+
+  if (Ctx.DependentNameTypes.size() != CP.DependentNameTypesSize) {
+    llvm::dbgs()
+        << "Ctx.DependentNameTypes.size() != CP.DependentNameTypesSize\n";
+    eraseFoldingSetIf(Ctx.DependentNameTypes,
+                      [&](DependentNameType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentNameTypes.size() == CP.DependentNameTypesSize);
+  }
+
+  if (Ctx.PackExpansionTypes.size() != CP.PackExpansionTypesSize) {
+    llvm::dbgs()
+        << "Ctx.PackExpansionTypes.size() != CP.PackExpansionTypesSize\n";
+    eraseFoldingSetIf(Ctx.PackExpansionTypes,
+                      [&](PackExpansionType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.PackExpansionTypes.size() == CP.PackExpansionTypesSize);
+  }
+
+  // mutable llvm::FoldingSet<ObjCObjectTypeImpl> ObjCObjectTypes;
+  // mutable llvm::FoldingSet<ObjCObjectPointerType> ObjCObjectPointerTypes;
+
+  if (Ctx.UnaryTransformTypes.size() != CP.UnaryTransformTypesSize) {
+    llvm::dbgs()
+        << "Ctx.UnaryTransformTypes.size() != CP.UnaryTransformTypesSize\n";
+    eraseFoldingSetIf(Ctx.UnaryTransformTypes,
+                      [&](UnaryTransformType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.UnaryTransformTypes.size() == CP.UnaryTransformTypesSize);
+  }
+
+  if (Ctx.AutoTypes.size() != CP.AutoTypesSize) {
+    llvm::dbgs() << "Ctx.AutoTypes.size() != CP.AutoTypesSize\n";
+    // mutable llvm::DenseMap<llvm::FoldingSetNodeID, AutoType *> AutoTypes;
+    eraseDenseMapIf(
+        Ctx.AutoTypes,
+        [&](llvm::detail::DenseMapPair<llvm::FoldingSetNodeID, AutoType *> &KV)
+            -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+              static_cast<void *>(KV.getSecond()), SlabCP);
+        });
+    assert(Ctx.AutoTypes.size() == CP.AutoTypesSize);
+  }
+
+  if (Ctx.DeducedTemplateSpecializationTypes.size() !=
+      CP.DeducedTemplateSpecializationTypesSize) {
+    llvm::dbgs() << "Ctx.DeducedTemplateSpecializationTypes.size() != "
+                    "CP.DeducedTemplateSpecializationTypesSize\n";
+    eraseFoldingSetIf(Ctx.DeducedTemplateSpecializationTypes,
+                      [&](DeducedTemplateSpecializationType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DeducedTemplateSpecializationTypes.size() ==
+           CP.DeducedTemplateSpecializationTypesSize);
+  }
+
+  if (Ctx.AtomicTypes.size() != CP.AtomicTypesSize) {
+    llvm::dbgs() << "Ctx.AtomicTypes.size() != CP.AtomicTypesSize\n";
+    eraseFoldingSetIf(Ctx.AtomicTypes, [&](AtomicType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.AtomicTypes.size() == CP.AtomicTypesSize);
+  }
+
+  if (Ctx.AttributedTypes.size() != CP.AttributedTypesSize) {
+    llvm::dbgs() << "Ctx.AttributedTypes.size() != CP.AttributedTypesSize\n";
+    eraseFoldingSetIf(Ctx.AttributedTypes, [&](AttributedType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.AttributedTypes.size() == CP.AttributedTypesSize);
+  }
+
+  if (Ctx.PipeTypes.size() != CP.PipeTypesSize) {
+    llvm::dbgs() << "Ctx.PipeTypes.size() != CP.PipeTypesSize\n";
+    eraseFoldingSetIf(Ctx.PipeTypes, [&](PipeType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.PipeTypes.size() == CP.PipeTypesSize);
+  }
+
+  if (Ctx.BitIntTypes.size() != CP.BitIntTypesSize) {
+    llvm::dbgs() << "Ctx.BitIntTypes.size() != CP.BitIntTypesSize\n";
+    eraseFoldingSetIf(Ctx.BitIntTypes, [&](BitIntType &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(Ctx.BitIntTypes.size() == CP.BitIntTypesSize);
+  }
+
+  if (Ctx.DependentBitIntTypes.size() != CP.DependentBitIntTypesSize) {
+    llvm::dbgs()
+        << "Ctx.DependentBitIntTypes.size() != CP.DependentBitIntTypesSize\n";
+    eraseFoldingSetIf(Ctx.DependentBitIntTypes,
+                      [&](DependentBitIntType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentBitIntTypes.size() == CP.DependentBitIntTypesSize);
+  }
+
+  if (Ctx.BTFTagAttributedTypes.size() != CP.BTFTagAttributedTypesSize) {
+    llvm::dbgs() << "Ctx.BTFTagAttributedTypes.size() != "
+                    "CP.BTFTagAttributedTypesSize\n";
+    eraseFoldingSetIf(Ctx.BTFTagAttributedTypes,
+                      [&](BTFTagAttributedType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.BTFTagAttributedTypes.size() == CP.BTFTagAttributedTypesSize);
+  }
+
+  if (Ctx.HLSLAttributedResourceTypes.size() !=
+      CP.HLSLAttributedResourceTypesSize) {
+    llvm::dbgs() << "Ctx.HLSLAttributedResourceTypes.size() != "
+                    "CP.HLSLAttributedResourceTypesSize\n";
+    eraseFoldingSetIf(Ctx.HLSLAttributedResourceTypes,
+                      [&](HLSLAttributedResourceType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.HLSLAttributedResourceTypes.size() ==
+           CP.HLSLAttributedResourceTypesSize);
+  }
+
+  if (Ctx.HLSLInlineSpirvTypes.size() != CP.HLSLInlineSpirvTypesSize) {
+    llvm::dbgs()
+        << "Ctx.HLSLInlineSpirvTypes.size() != CP.HLSLInlineSpirvTypesSize\n";
+    eraseFoldingSetIf(Ctx.HLSLInlineSpirvTypes,
+                      [&](HLSLInlineSpirvType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.HLSLInlineSpirvTypes.size() == CP.HLSLInlineSpirvTypesSize);
+  }
+
+  if (Ctx.CountAttributedTypes.size() != CP.CountAttributedTypesSize) {
+    llvm::dbgs()
+        << "Ctx.CountAttributedTypes.size() != CP.CountAttributedTypesSize\n";
+    eraseFoldingSetIf(Ctx.CountAttributedTypes,
+                      [&](CountAttributedType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.CountAttributedTypes.size() == CP.CountAttributedTypesSize);
+  }
+
+  if (Ctx.QualifiedTemplateNames.size() != CP.QualifiedTemplateNamesSize) {
+    llvm::dbgs() << "Ctx.QualifiedTemplateNames.size() != "
+                    "CP.QualifiedTemplateNamesSize\n";
+    eraseFoldingSetIf(Ctx.QualifiedTemplateNames,
+                      [&](QualifiedTemplateName &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.QualifiedTemplateNames.size() == CP.QualifiedTemplateNamesSize);
+  }
+
+  if (Ctx.DependentTemplateNames.size() != CP.DependentTemplateNamesSize) {
+    llvm::dbgs() << "Ctx.DependentTemplateNames.size() != "
+                    "CP.DependentTemplateNamesSize\n";
+    eraseFoldingSetIf(Ctx.DependentTemplateNames,
+                      [&](DependentTemplateName &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DependentTemplateNames.size() == CP.DependentTemplateNamesSize);
+  }
+
+  if (Ctx.SubstTemplateTemplateParms.size() !=
+      CP.SubstTemplateTemplateParmsSize) {
+    llvm::dbgs() << "Ctx.SubstTemplateTemplateParms.size() != "
+                    "CP.SubstTemplateTemplateParmsSize\n";
+    eraseFoldingSetIf(Ctx.SubstTemplateTemplateParms,
+                      [&](SubstTemplateTemplateParmStorage &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.SubstTemplateTemplateParms.size() ==
+           CP.SubstTemplateTemplateParmsSize);
+  }
+
+  if (Ctx.SubstTemplateTemplateParmPacks.size() !=
+      CP.SubstTemplateTemplateParmPacksSize) {
+    llvm::dbgs() << "Ctx.SubstTemplateTemplateParmPacks.size() != "
+                    "CP.SubstTemplateTemplateParmPacksSize\n";
+    eraseFoldingSetIf(Ctx.SubstTemplateTemplateParmPacks,
+                      [&](SubstTemplateTemplateParmPackStorage &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.SubstTemplateTemplateParmPacks.size() ==
+           CP.SubstTemplateTemplateParmPacksSize);
+  }
+
+  if (Ctx.DeducedTemplates.size() != CP.DeducedTemplatesSize) {
+    llvm::dbgs() << "Ctx.DeducedTemplates.size() != CP.DeducedTemplatesSize\n";
+    eraseFoldingSetIf(Ctx.DeducedTemplates,
+                      [&](DeducedTemplateStorage &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.DeducedTemplates.size() == CP.DeducedTemplatesSize);
+  }
+
+  if (Ctx.ArrayParameterTypes.size() != CP.ArrayParameterTypesSize) {
+    llvm::dbgs()
+        << "Ctx.ArrayParameterTypes.size() != CP.ArrayParameterTypesSize\n";
+    eraseFoldingSetIf(Ctx.ArrayParameterTypes,
+                      [&](ArrayParameterType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(Ctx.ArrayParameterTypes.size() == CP.ArrayParameterTypesSize);
+  }
+
+  if (CP.PredefinedSugarTypesSize != Ctx.PredefinedSugarTypes.size()) {
+    llvm::dbgs() << "if (CP.PredefinedSugarTypesSize != "
+                    "Ctx.PredefinedSugarTypes.size()\n";
+    //                 mutable std::array<Type *,
+    //                llvm::to_underlying(PredefinedSugarType::Kind::Last) +
+    //                1>
+    // PredefinedSugarTypes{};
+    assert(CP.PredefinedSugarTypesSize == Ctx.PredefinedSugarTypes.size());
+  }
+
+  if (CP.NamespaceAndPrefixStoragesSize !=
+      Ctx.NamespaceAndPrefixStorages.size()) {
+    llvm::dbgs() << "if (CP.NamespaceAndPrefixStoragesSize != "
+                    "Ctx.NamespaceAndPrefixStorages.size()\n";
+    eraseFoldingSetIf(Ctx.NamespaceAndPrefixStorages,
+                      [&](NamespaceAndPrefixStorage &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(CP.NamespaceAndPrefixStoragesSize ==
+           Ctx.NamespaceAndPrefixStorages.size());
+  }
+
+  if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()) {
+    llvm::dbgs()
+        << "if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()\n";
+    eraseDenseMapIf(
+        Ctx.ASTRecordLayouts,
+        [&](llvm::detail::DenseMapPair<const RecordDecl *,
+                                       const ASTRecordLayout *> &KV) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+                     static_cast<void *>(
+                         const_cast<RecordDecl *>(KV.getFirst())),
+                     SlabCP) ||
+                 Ctx.getAllocator().isAfterCheckpoint(
+                     static_cast<void *>(
+                         const_cast<ASTRecordLayout *>(KV.getSecond())),
+                     SlabCP);
+        });
+    assert(CP.ASTRecordLayoutsSize == Ctx.ASTRecordLayouts.size());
+  }
+
+  if (CP.MemoizedTypeInfoSize != Ctx.MemoizedTypeInfo.size()) {
+    llvm::dbgs()
+        << "if (CP.MemoizedTypeInfoSize != Ctx.MemoizedTypeInfo.size() "
+        << CP.MemoizedTypeInfoSize << " != " << Ctx.MemoizedTypeInfo.size()
+        << " \n";
+    eraseDenseMapIf(
+        Ctx.MemoizedTypeInfo,
+        [&](llvm::detail::DenseMapPair<const Type *, struct TypeInfo> &KV)
+            -> bool {
+          // llvm::outs() << "Type * In Range : [ "
+          //              << static_cast<void *>(SlabCP.CurPtr) << " < "
+          //              << KV.getFirst() << " < "
+          //              << static_cast<void *>(SlabCP.End) << "]\n";
+          return Ctx.getAllocator().isAfterCheckpoint(
+              static_cast<void *>(const_cast<Type *>(KV.getFirst())), SlabCP);
+        });
+    // assert(CP.MemoizedTypeInfoSize == Ctx.MemoizedTypeInfo.size());
+  }
+
+  if (CP.MemoizedUnadjustedAlignSize != Ctx.MemoizedUnadjustedAlign.size()) {
+    llvm::dbgs() << "if (CP.MemoizedUnadjustedAlignSize != "
+                    "Ctx.MemoizedUnadjustedAlign.size()\n";
+    eraseDenseMapIf(
+        Ctx.MemoizedUnadjustedAlign,
+        [&](llvm::detail::DenseMapPair<const Type *, unsigned> &KV) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+              static_cast<void *>(const_cast<Type *>(KV.getFirst())), SlabCP);
+        });
+    assert(CP.MemoizedUnadjustedAlignSize ==
+           Ctx.MemoizedUnadjustedAlign.size());
+  }
+
+  if (CP.KeyFunctionsSize != Ctx.KeyFunctions.size()) {
+    llvm::dbgs() << "if (CP.KeyFunctionsSize != Ctx.KeyFunctions.size()\n";
+    eraseDenseMapIf(
+        Ctx.KeyFunctions,
+        [&](llvm::detail::DenseMapPair<const CXXRecordDecl *, LazyDeclPtr> &KV)
+            -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+              static_cast<void *>(const_cast<CXXRecordDecl *>(KV.getFirst())),
+              SlabCP);
+        });
+    assert(CP.KeyFunctionsSize == Ctx.KeyFunctions.size());
+  }
+
+  if (CP.BlockVarCopyInitsSize != Ctx.BlockVarCopyInits.size()) {
+    llvm::dbgs()
+        << "if (CP.BlockVarCopyInitsSize != Ctx.BlockVarCopyInits.size()\n";
+    eraseDenseMapIf(
+        Ctx.BlockVarCopyInits,
+        [&](llvm::detail::DenseMapPair<const VarDecl *, BlockVarCopyInit> &KV)
+            -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+              static_cast<void *>(const_cast<VarDecl *>(KV.getFirst())),
+              SlabCP);
+        });
+    assert(CP.BlockVarCopyInitsSize == Ctx.BlockVarCopyInits.size());
+  }
+
+  if (CP.MSGuidDeclsSize != Ctx.MSGuidDecls.size()) {
+    llvm::dbgs() << "if (CP.MSGuidDeclsSize != Ctx.MSGuidDecls.size()\n";
+    eraseFoldingSetIf(Ctx.MSGuidDecls, [&](MSGuidDecl &Node) -> bool {
+      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+                                                  SlabCP);
+    });
+    assert(CP.MSGuidDeclsSize == Ctx.MSGuidDecls.size());
+  }
+
+  if (CP.UnnamedGlobalConstantDeclsSize !=
+      Ctx.UnnamedGlobalConstantDecls.size()) {
+    llvm::dbgs() << "if (CP.UnnamedGlobalConstantDeclsSize != "
+                    "Ctx.UnnamedGlobalConstantDecls.size()\n";
+    eraseFoldingSetIf(Ctx.UnnamedGlobalConstantDecls,
+                      [&](UnnamedGlobalConstantDecl &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(CP.UnnamedGlobalConstantDeclsSize ==
+           Ctx.UnnamedGlobalConstantDecls.size());
+  }
+
+  if (CP.TemplateParamObjectDeclsSize != Ctx.TemplateParamObjectDecls.size()) {
+    llvm::dbgs() << "if (CP.TemplateParamObjectDeclsSize != "
+                    "Ctx.TemplateParamObjectDecls.size()\n";
+    eraseFoldingSetIf(Ctx.TemplateParamObjectDecls,
+                      [&](TemplateParamObjectDecl &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(CP.TemplateParamObjectDeclsSize ==
+           Ctx.TemplateParamObjectDecls.size());
+  }
+
+  if (CP.StringLiteralCacheSize != Ctx.StringLiteralCache.size()) {
+    llvm::dbgs()
+        << "if (CP.StringLiteralCacheSize != Ctx.StringLiteralCache.size()\n";
+    //   mutable llvm::StringMap<StringLiteral *> StringLiteralCache;
+    assert(CP.StringLiteralCacheSize == Ctx.StringLiteralCache.size());
+  }
+
+  if (CP.DestroyingOperatorDeletesSize !=
+      Ctx.DestroyingOperatorDeletes.size()) {
+    llvm::dbgs() << "if (CP.DestroyingOperatorDeletesSize != "
+                    "Ctx.DestroyingOperatorDeletes.size()\n";
+    eraseDenseSetIf(
+        Ctx.DestroyingOperatorDeletes, [&](const FunctionDecl *FD) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+              static_cast<void *>(const_cast<FunctionDecl *>(FD)), SlabCP);
+        });
+    assert(CP.DestroyingOperatorDeletesSize ==
+           Ctx.DestroyingOperatorDeletes.size());
+  }
+
+  if (CP.TypeAwareOperatorNewAndDeletesSize !=
+      Ctx.TypeAwareOperatorNewAndDeletes.size()) {
+    llvm::dbgs() << "CP.TypeAwareOperatorNewAndDeletesSize != "
+                    "Ctx.TypeAwareOperatorNewAndDeletes.size()\n";
+    eraseDenseSetIf(Ctx.TypeAwareOperatorNewAndDeletes,
+                    [&](const FunctionDecl *FD) -> bool {
+                      return Ctx.getAllocator().isAfterCheckpoint(
+                          static_cast<void *>(const_cast<FunctionDecl *>(FD)),
+                          SlabCP);
+                    });
+    assert(CP.TypeAwareOperatorNewAndDeletesSize ==
+           Ctx.TypeAwareOperatorNewAndDeletes.size());
+  }
+
+  if (CP.OperatorDeletesForVirtualDtorSize !=
+      Ctx.OperatorDeletesForVirtualDtor.size()) {
+    llvm::dbgs() << "CP.OperatorDeletesForVirtualDtorSize != "
+                    "Ctx.OperatorDeletesForVirtualDtor.size()\n";
+    eraseDenseMapIf(
+        Ctx.OperatorDeletesForVirtualDtor,
+        [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
+                                       FunctionDecl *> &KV) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+                     static_cast<void *>(
+                         const_cast<CXXDestructorDecl *>(KV.getFirst())),
+                     SlabCP) ||
+                 Ctx.getAllocator().isAfterCheckpoint(
+                     static_cast<void *>(KV.getSecond()), SlabCP);
+        });
+    assert(CP.OperatorDeletesForVirtualDtorSize ==
+           Ctx.OperatorDeletesForVirtualDtor.size());
+  }
+
+  if (CP.GlobalOperatorDeletesForVirtualDtorSize !=
+      Ctx.GlobalOperatorDeletesForVirtualDtor.size()) {
+    llvm::dbgs() << "CP.GlobalOperatorDeletesForVirtualDtorSize != "
+                    "Ctx.GlobalOperatorDeletesForVirtualDtor.size()\n";
+    eraseDenseMapIf(
+        Ctx.GlobalOperatorDeletesForVirtualDtor,
+        [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
+                                       FunctionDecl *> &KV) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+                     static_cast<void *>(
+                         const_cast<CXXDestructorDecl *>(KV.getFirst())),
+                     SlabCP) ||
+                 Ctx.getAllocator().isAfterCheckpoint(
+                     static_cast<void *>(KV.getSecond()), SlabCP);
+        });
+    assert(CP.GlobalOperatorDeletesForVirtualDtorSize ==
+           Ctx.GlobalOperatorDeletesForVirtualDtor.size());
+  }
+
+  if (CP.ArrayOperatorDeletesForVirtualDtorSize !=
+      Ctx.ArrayOperatorDeletesForVirtualDtor.size()) {
+    llvm::dbgs() << "CP.ArrayOperatorDeletesForVirtualDtorSize != "
+                    "Ctx.ArrayOperatorDeletesForVirtualDtor.size()\n";
+    eraseDenseMapIf(
+        Ctx.ArrayOperatorDeletesForVirtualDtor,
+        [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
+                                       FunctionDecl *> &KV) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+                     static_cast<void *>(
+                         const_cast<CXXDestructorDecl *>(KV.getFirst())),
+                     SlabCP) ||
+                 Ctx.getAllocator().isAfterCheckpoint(
+                     static_cast<void *>(KV.getSecond()), SlabCP);
+        });
+    assert(CP.ArrayOperatorDeletesForVirtualDtorSize ==
+           Ctx.ArrayOperatorDeletesForVirtualDtor.size());
+  }
+
+  if (CP.GlobalArrayOperatorDeletesForVirtualDtorSize !=
+      Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size()) {
+    llvm::dbgs() << "CP.GlobalArrayOperatorDeletesForVirtualDtorSize != "
+                    "Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size()\n";
+    eraseDenseMapIf(
+        Ctx.GlobalArrayOperatorDeletesForVirtualDtor,
+        [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
+                                       FunctionDecl *> &KV) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+                     static_cast<void *>(
+                         const_cast<CXXDestructorDecl *>(KV.getFirst())),
+                     SlabCP) ||
+                 Ctx.getAllocator().isAfterCheckpoint(
+                     static_cast<void *>(KV.getSecond()), SlabCP);
+        });
+    assert(CP.GlobalArrayOperatorDeletesForVirtualDtorSize ==
+           Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size());
+  }
+
+  if (CP.RequireVectorDeletingDtorSize !=
+      Ctx.RequireVectorDeletingDtor.size()) {
+    llvm::dbgs() << "if (CP.RequireVectorDeletingDtorSize != "
+                    "Ctx.RequireVectorDeletingDtor.size()\n";
+    eraseDenseSetIf(
+        Ctx.RequireVectorDeletingDtor, [&](const CXXRecordDecl *RD) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+              static_cast<void *>(const_cast<CXXRecordDecl *>(RD)), SlabCP);
+        });
+    assert(CP.RequireVectorDeletingDtorSize ==
+           Ctx.RequireVectorDeletingDtor.size());
+  }
+
+  if (CP.MergedDeclsSize != Ctx.MergedDecls.size()) {
+    llvm::dbgs() << "CP.MergedDeclsSize != Ctx.MergedDecls.size()";
+    assert(CP.MergedDeclsSize == Ctx.MergedDecls.size());
+  }
+
+  if (CP.MergedDefModulesSize != Ctx.MergedDefModules.size()) {
+    llvm::dbgs() << "CP.MergedDefModulesSize != Ctx.MergedDefModules.size()";
+    assert(CP.MergedDefModulesSize == Ctx.MergedDefModules.size());
+  }
+
+  if (CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()) {
+    llvm::dbgs()
+        << "CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()";
+    assert(CP.ModuleInitializersSize == Ctx.ModuleInitializers.size());
+  }
+
+  if (CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()) {
+    llvm::dbgs()
+        << "CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()";
+    assert(CP.PrimaryModuleNameMapSize == Ctx.PrimaryModuleNameMap.size());
+  }
+
+  if (CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()) {
+    llvm::dbgs()
+        << "CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()";
+    assert(CP.SameModuleLookupSetSize == Ctx.SameModuleLookupSet.size());
+  }
+
+  if (CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()) {
+    llvm::dbgs() << "CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()";
+    assert(CP.ScalableVecTyMapSize == Ctx.ScalableVecTyMap.size());
+  }
+
+  if (CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()) {
+    llvm::dbgs() << "CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()";
+    assert(CP.LambdaCastPathsSize == Ctx.LambdaCastPaths.size());
+  }
+
+  if (CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()) {
+    llvm::dbgs() << "CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()";
+    assert(CP.DeclRawCommentsSize == Ctx.DeclRawComments.size());
+  }
+
+  if (CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()) {
+    llvm::dbgs()
+        << "CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()";
+    assert(CP.RedeclChainCommentsSize == Ctx.RedeclChainComments.size());
+  }
+
+  if (CP.CommentlessRedeclChainsSize != Ctx.CommentlessRedeclChains.size()) {
+    llvm::dbgs() << "CP.CommentlessRedeclChainsSize != "
+                    "Ctx.CommentlessRedeclChains.size()";
+    assert(CP.CommentlessRedeclChainsSize ==
+           Ctx.CommentlessRedeclChains.size());
+  }
+
+  if (CP.ParsedCommentsSize != Ctx.ParsedComments.size()) {
+    llvm::dbgs() << "CP.ParsedCommentsSize != Ctx.ParsedComments.size()";
+    assert(CP.ParsedCommentsSize == Ctx.ParsedComments.size());
+  }
+
+  if (CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()) {
+    llvm::dbgs()
+        << "CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()";
+    assert(CP.RelocatableClassesSize == Ctx.RelocatableClasses.size());
+  }
+
+  if (CP.ParamIndicesSize != Ctx.ParamIndices.size()) {
+    llvm::dbgs() << "CP.ParamIndicesSize != Ctx.ParamIndices.size()";
+    assert(CP.ParamIndicesSize == Ctx.ParamIndices.size());
+  }
+
+  if (CP.MangleNumbersSize != Ctx.MangleNumbers.size()) {
+    llvm::dbgs() << "CP.MangleNumbersSize != Ctx.MangleNumbers.size()";
+    assert(CP.MangleNumbersSize == Ctx.MangleNumbers.size());
+  }
+
+  if (CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()) {
+    llvm::dbgs()
+        << "CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()";
+    assert(CP.StaticLocalNumbersSize == Ctx.StaticLocalNumbers.size());
+  }
+
+  if (CP.TemplateOrInstantiationSize != Ctx.TemplateOrInstantiation.size()) {
+    llvm::dbgs() << "CP.TemplateOrInstantiationSize != "
+                    "Ctx.TemplateOrInstantiation.size()";
+    assert(CP.TemplateOrInstantiationSize ==
+           Ctx.TemplateOrInstantiation.size());
+  }
+
+  if (CP.InstantiatedFromUsingDeclSize !=
+      Ctx.InstantiatedFromUsingDecl.size()) {
+    llvm::dbgs() << "CP.InstantiatedFromUsingDeclSize != "
+                    "Ctx.InstantiatedFromUsingDecl.size()";
+    assert(CP.InstantiatedFromUsingDeclSize ==
+           Ctx.InstantiatedFromUsingDecl.size());
+  }
+
+  if (CP.InstantiatedFromUsingEnumDeclSize !=
+      Ctx.InstantiatedFromUsingEnumDecl.size()) {
+    llvm::dbgs() << "CP.InstantiatedFromUsingEnumDeclSize != "
+                    "Ctx.InstantiatedFromUsingEnumDecl.size()";
+    assert(CP.InstantiatedFromUsingEnumDeclSize ==
+           Ctx.InstantiatedFromUsingEnumDecl.size());
+  }
+
+  if (CP.InstantiatedFromUsingShadowDeclSize !=
+      Ctx.InstantiatedFromUsingShadowDecl.size()) {
+    llvm::dbgs() << "CP.InstantiatedFromUsingShadowDeclSize != "
+                    "Ctx.InstantiatedFromUsingShadowDecl.size()";
+    assert(CP.InstantiatedFromUsingShadowDeclSize ==
+           Ctx.InstantiatedFromUsingShadowDecl.size());
+  }
+
+  if (CP.InstantiatedFromUnnamedFieldDeclSize !=
+      Ctx.InstantiatedFromUnnamedFieldDecl.size()) {
+    llvm::dbgs() << "CP.InstantiatedFromUnnamedFieldDeclSize != "
+                    "Ctx.InstantiatedFromUnnamedFieldDecl.size()";
+    assert(CP.InstantiatedFromUnnamedFieldDeclSize ==
+           Ctx.InstantiatedFromUnnamedFieldDecl.size());
+  }
+
+  if (CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()) {
+    llvm::dbgs() << "CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()";
+    assert(CP.OverriddenMethodsSize == Ctx.OverriddenMethods.size());
+  }
+
+  if (CP.MangleNumberingContextsSize != Ctx.MangleNumberingContexts.size()) {
+    llvm::dbgs() << "CP.MangleNumberingContextsSize != "
+                    "Ctx.MangleNumberingContexts.size()";
+    assert(CP.MangleNumberingContextsSize ==
+           Ctx.MangleNumberingContexts.size());
+  }
+
+  if (CP.ExtraMangleNumberingContextsSize !=
+      Ctx.ExtraMangleNumberingContexts.size()) {
+    llvm::dbgs() << "CP.ExtraMangleNumberingContextsSize != "
+                    "Ctx.ExtraMangleNumberingContexts.size()";
+    assert(CP.ExtraMangleNumberingContextsSize ==
+           Ctx.ExtraMangleNumberingContexts.size());
+  }
+
+  if (CP.TraversalScopeSize != Ctx.TraversalScope.size()) {
+    llvm::dbgs() << "CP.TraversalScopeSize != "
+                    "Ctx.TraversalScope.size()";
+    assert(CP.TraversalScopeSize == Ctx.TraversalScope.size());
+  }
+
+  for (auto &[Decl, OldTy] : Ctx.PendingTypeForDeclMutations)
+    Decl->TypeForDecl = OldTy;
+
+  Ctx.PendingTypeForDeclMutations.clear();
+  TranslationUnitDecl *MostRecentTU = Ctx.getTranslationUnitDecl();
+  for (const auto *DC : Ctx.PendingDCMutations) {
+    if (MostRecentTU->getPrimaryContext() == DC)
+      continue;
+    if (StoredDeclsMap *Map = const_cast<DeclContext *>(DC)
+                                  ->getPrimaryContext()
+                                  ->getLookupPtr()) {
+      for (auto &&[Key, List] : *Map) {
+        DeclContextLookupResult R = List.getLookupResult();
+        std::vector<NamedDecl *> NamedDeclsToRemove;
+        // bool RemoveAll = true;
+        for (NamedDecl *D : R) {
+          // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU (" << (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
+          // llvm::outs() << "DeclContext : " << DC << "\n";
+          // D->dump();
+          // if (D->getTranslationUnitDecl() == MostRecentTU)
+          if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D),
+                                                   SlabCP))
+            NamedDeclsToRemove.push_back(D);
+          // else
+          //   RemoveAll = false;
+        }
+        // if (LLVM_LIKELY(RemoveAll)) {
+        //   Map->erase(Key);
+        // } else {
+        for (NamedDecl *D : NamedDeclsToRemove)
+          List.remove(D);
+        // }
+      }
+    }
+
+    Decl *Prev = DC->FirstDecl;
+    Decl *Cur = DC->FirstDecl;
+    while (Cur) {
+      if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
+                                               SlabCP)) {
+        DC->LastDecl = Prev;
+        DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
+        break;
+      }
+      Prev = Cur;
+      Cur = Cur->getNextDeclInContext();
+    }
+  }
+
+  // if (FirstDecl) {
+  //   LastDecl->NextInContextAndBits.setPointer(D);
+  //   LastDecl = D;
+  // } else {
+  //   FirstDecl = LastDecl = D;
+  // }
+
+  // Notify a C++ record declaration that we've added a member, so it can
+  // update its class-specific state.
+  // if (auto *Record = dyn_cast<CXXRecordDecl>(this))
+  //   Record->addedMember(D);
+
+
+  //   ImportDecl *FirstLocalImport = nullptr;
+  // ImportDecl *LastLocalImport = nullptr;
+
+
+  // class ImportDecl final : public Decl,
+  //                        llvm::TrailingObjects<ImportDecl, SourceLocation> {
+  // friend class ASTContext;
+  // friend class ASTDeclReader;
+  // friend class ASTReader;
+  // friend TrailingObjects;
+
+  // /// The imported module.
+  // Module *ImportedModule = nullptr;
+
+  // /// The next import in the list of imports local to the translation
+  // /// unit being parsed (not loaded from an AST file).
+  // ///
+  // /// Includes a bit that indicates whether we have source-location information
+  // /// for each identifier in the module name.
+  // ///
+  // /// When the bit is false, we only have a single source location for the
+  // /// end of the import declaration.
+  // llvm::PointerIntPair<ImportDecl *, 1, bool> NextLocalImportAndComplete;
+
+  Ctx.PendingDCMutations.clear();
+
+  llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM = Ctx.LastSDM;
+
+  StoredDeclsMap *Map = LastSDM.getPointer();
+  bool Dependent = LastSDM.getInt();
+  while (Map && (Map != CP.LastSDM.getPointer())) {
+    // llvm::outs() << "we are deleting LASTSDM\n";
+  //   // Advance the iteration before we invalidate memory.
+    llvm::PointerIntPair<StoredDeclsMap*,1> Next = Map->Previous;
+
+    if (Dependent)
+      delete static_cast<DependentStoredDeclsMap*>(Map);
+    else
+      delete Map;
+
+    Map = Next.getPointer();
+    Dependent = Next.getInt();
+  }
+
+  Ctx.LastSDM = CP.LastSDM;
+
+  Ctx.Types.resize(CP.TypesSize);
+}
+
+void ASTContextStateStash::commit() {
+  Ctx.PendingTypeForDeclMutations.clear();
+  Ctx.PendingDCMutations.clear();
+}
+} // end namespace clang
\ No newline at end of file
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index 01d3295d1ac30..ab8e418e8d731 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -20,12 +20,14 @@ if (EMSCRIPTEN AND "lld" IN_LIST LLVM_ENABLE_PROJECTS)
 endif()
 
 add_clang_library(clangInterpreter
+  ASTContextStateStash.cpp
   DeviceOffload.cpp
   CodeCompletion.cpp
   IncrementalAction.cpp
   IncrementalExecutor.cpp
   OrcIncrementalExecutor.cpp
   IncrementalParser.cpp
+  SemaStateStash.cpp
   Interpreter.cpp
   InterpreterValuePrinter.cpp
   InterpreterUtils.cpp
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index 3cfabb5e5daa1..bdd9d41b0ce48 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -329,6 +329,8 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
 
   // Lookup alone is not enough: the redeclaration chain still reaches these.
   withdrawMostRecentTU(MostRecentTU);
+  RepairRedeclChain(MostRecentTU, MostRecentTU);
+  S.getASTContext().setTranslationUnitDecl(MostRecentTU->getPreviousDecl());
 }
 
 PartialTranslationUnit &
diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h
index b626cebaafcd7..e6eaa7dddb7fd 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -63,6 +63,9 @@ class IncrementalParser {
 
   void CleanUpPTU(TranslationUnitDecl *MostRecentTU);
 
+  template <typename decl_type>
+  void RepairRedeclChain(decl_type *D, TranslationUnitDecl *PTU);
+
   /// 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 655db32477a0c..f951425c9c862 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -35,6 +35,7 @@
 #include "clang/Frontend/MultiplexConsumer.h"
 #include "clang/Frontend/TextDiagnosticBuffer.h"
 #include "clang/FrontendTool/Utils.h"
+#include "clang/Interpreter/ErrorRecovery.h"
 #include "clang/Interpreter/IncrementalExecutor.h"
 #include "clang/Interpreter/Interpreter.h"
 #include "clang/Interpreter/Value.h"
@@ -567,6 +568,39 @@ size_t Interpreter::getEffectivePTUSize() const {
 
 llvm::Expected<PartialTranslationUnit &>
 Interpreter::Parse(llvm::StringRef Code) {
+  class PTUSlabRollback {
+  public:
+    explicit PTUSlabRollback(Sema &S)
+        : Ctx(S.getASTContext()), ASTCtxState(Ctx), SemaState(S),
+          CheckPoint(Ctx.getAllocator().checkPoint()) {
+            SemaState.stash(SemaCheckPoint);
+            ASTCtxState.stash(CtxCheckPoint);
+          }
+
+    ~PTUSlabRollback() {
+      if (!Committed) {
+        SemaState.restore(SemaCheckPoint, CheckPoint);
+        ASTCtxState.restore(CtxCheckPoint, CheckPoint);
+        Ctx.getAllocator().restoreToCheckPoint(CheckPoint);
+      }
+    }
+
+    void commit(PartialTranslationUnit &PTU) {
+      ASTCtxState.commit();
+      PTU.SlabCheckPoint = CheckPoint;
+      Committed = true;
+    }
+
+  private:
+    ASTContext &Ctx;
+    ASTContextStateStash ASTCtxState;
+    SemaStateStash SemaState;
+    llvm::SlabCheckPoint CheckPoint;
+    bool Committed = false;
+    StashCheckPoint CtxCheckPoint;
+    SemaStashCheckPoint SemaCheckPoint;
+  };
+
   // If we have a device parser, parse it first. The generated code will be
   // included in the host compilation
   if (DeviceParser) {
@@ -590,6 +624,8 @@ Interpreter::Parse(llvm::StringRef Code) {
   getCompilerInstance()->getDiagnostics().setSeverity(
       clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation());
 
+  PTUSlabRollback Rollback(CI->getSema());
+
   llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
   if (!TuOrErr)
     return TuOrErr.takeError();
@@ -602,6 +638,7 @@ Interpreter::Parse(llvm::StringRef Code) {
           frontend::EmitLLVM)
     LastPTU.TheModule->print(llvm::outs(), /*AAW=*/nullptr);
 
+  Rollback.commit(LastPTU);
   return LastPTU;
 }
 
diff --git a/clang/lib/Interpreter/SemaStateStash.cpp b/clang/lib/Interpreter/SemaStateStash.cpp
new file mode 100644
index 0000000000000..8dce1182c4279
--- /dev/null
+++ b/clang/lib/Interpreter/SemaStateStash.cpp
@@ -0,0 +1,882 @@
+//===--- SemaStateStash.cpp - Sema persistent state stash/restore
+//----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Interpreter/ErrorRecovery.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaInternal.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang {
+
+// template <typename EntryType, typename PredT>
+// static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred)
+// {
+//   SmallVector<EntryType *, 16> ToRemove;
+//   for (auto &N : FS)
+//     if (Pred(N))
+//       ToRemove.push_back(&N);
+//   for (auto *N : ToRemove)
+//     FS.RemoveNode(N);
+// }
+
+// template <typename EntryType, typename PredT>
+// static void eraseContextualFoldingSetIf(
+//     llvm::ContextualFoldingSet<EntryType, const ASTContext &> &FS,
+//     PredT &&Pred) {
+//   SmallVector<EntryType *, 16> ToRemove;
+//   for (auto &N : FS)
+//     if (Pred(N))
+//       ToRemove.push_back(&N);
+//   for (auto *N : ToRemove)
+//     FS.RemoveNode(N);
+// }
+
+/// Erase from DenseMap based on predicate
+template <typename KeyT, typename ValueT, typename PredT>
+static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
+  SmallVector<KeyT, 16> ToRemove;
+
+  for (auto &KV : Map)
+    if (Pred(KV))
+      ToRemove.push_back(KV.getFirst());
+
+  for (auto &Key : ToRemove)
+    Map.erase(Key);
+}
+
+// template <typename ValueT, typename PredT>
+// static void eraseDenseSetIf(llvm::DenseSet<ValueT> &Set, PredT &&Pred) {
+//   SmallVector<ValueT, 16> ToRemove;
+//   for (const auto &Val : Set)
+//     if (Pred(Val))
+//       ToRemove.push_back(Val);
+//   for (const auto &Val : ToRemove)
+//     Set.erase(Val);
+// }
+
+// template <typename T, typename PredT>
+// static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, 4> &Set, PredT &&Pred) {
+//   SmallVector<T, 8> ToRemove;
+//   for (T Val : Set)
+//     if (Pred(Val))
+//       ToRemove.push_back(Val);
+//   for (T Val : ToRemove)
+//     Set.erase(Val);
+// }
+
+// template <typename T, unsigned N, typename PredT>
+// static void eraseSmallSetVectorIf(llvm::SmallSetVector<T, N> &SV,
+//                                   PredT &&Pred) {
+//   SmallVector<T, 8> ToRemove;
+//   for (const T &Val : SV)
+//     if (Pred(Val))
+//       ToRemove.push_back(Val);
+//   for (const T &Val : ToRemove)
+//     SV.remove(Val);
+// }
+
+// template <typename KeyT, typename ValueT, typename PredT>
+// static void eraseMapVectorIf(llvm::MapVector<KeyT, ValueT> &MV, PredT &&Pred)
+// {
+//   SmallVector<KeyT, 16> ToRemove;
+//   for (auto &KV : MV)
+//     if (Pred(KV))
+//       ToRemove.push_back(KV.first);
+//   for (const auto &Key : ToRemove)
+//     MV.erase(Key);
+// }
+
+// template <typename T, typename PredT>
+// static void eraseVectorIf(SmallVectorImpl<T> &Vec, PredT &&Pred) {
+//   llvm::erase_if(Vec, std::forward<PredT>(Pred));
+// }
+
+//===----------------------------------------------------------------------===//
+// Pragma / value snapshot (uses Sema friend access for nested types)
+//===----------------------------------------------------------------------===//
+
+// struct SemaStateStash::PragmaSnapshot {
+//   Sema::PragmaClangSection PragmaClangBSSSection;
+//   Sema::PragmaClangSection PragmaClangDataSection;
+//   Sema::PragmaClangSection PragmaClangRodataSection;
+//   Sema::PragmaClangSection PragmaClangRelroSection;
+//   Sema::PragmaClangSection PragmaClangTextSection;
+
+//   Sema::PragmaStack<MSVtorDispMode> VtorDispStack;
+//   Sema::PragmaStack<Sema::AlignPackInfo> AlignPackStack;
+//   Sema::PragmaStack<StringLiteral *> DataSegStack;
+//   Sema::PragmaStack<StringLiteral *> BSSSegStack;
+//   Sema::PragmaStack<StringLiteral *> ConstSegStack;
+//   Sema::PragmaStack<StringLiteral *> CodeSegStack;
+//   Sema::PragmaStack<bool> StrictGuardStackCheckStack;
+//   Sema::PragmaStack<FPOptionsOverride> FpPragmaStack;
+
+//   StringLiteral *CurInitSeg = nullptr;
+//   SourceLocation CurInitSegLoc;
+//   bool MSPragmaOptimizeIsOn = true;
+//   SourceLocation OptimizeOffPragmaLocation;
+
+//   FileNullabilityMap NullabilityMap;
+// };
+
+void SemaStateStash::stash(SemaStashCheckPoint &CP) {
+  CP.SemaBumpSlabCP = S.BumpAlloc.checkPoint();
+  // CP.CachedFunctionScopeSize = S.CachedFunctionScope.size();
+  CP.FunctionScopesSize = S.FunctionScopes.size();
+
+  // CP.Ident_superSize = S.Ident_super.size();
+
+  /// --- Pragma ---
+  // CP.PragmaClangBSSSectionSize = S.PragmaClangBSSSection.size();
+  // CP.PragmaClangDataSectionSize = S.PragmaClangDataSection.size();
+  // CP.PragmaClangRodataSectionSize = S.PragmaClangRodataSection.size();
+  // CP.PragmaClangRelroSectionSize = S.PragmaClangRelroSection.size();
+  // CP.PragmaClangTextSectionSize = S.PragmaClangTextSection.size();
+  // CP.VtorDispStackSize = S.VtorDispStack.size();
+  // CP.AlignPackStackSize = S.AlignPackStack.size();
+  // CP.AlignPackIncludeStackSize = S.AlignPackIncludeStack.size();
+  // CP.DataSegStackSize = S.DataSegStack.size();
+  // CP.BSSSegStackSize = S.BSSSegStack.size();
+  // CP.ConstSegStackSize = S.ConstSegStack.size();
+  // CP.CodeSegStackSize = S.CodeSegStack.size();
+  // CP.StrictGuardStackCheckStackSize = S.StrictGuardStackCheckStack.size();
+  // CP.FpPragmaStackSize = S.FpPragmaStack.size();
+
+  // CP.FunctionToSectionMapSize = S.FunctionToSectionMap.size();
+  // CP.PragmaAttributeStackSize = S.PragmaAttributeStack.size();
+  // CP.MSFunctionNoBuiltinsSize = S.MSFunctionNoBuiltins.size();
+  // CP.PendingExportedNamesSize = S.PendingExportedNames.size();
+  CP.TypoCorrectedFunctionDefinitionsSize =
+      S.TypoCorrectedFunctionDefinitions.size();
+  CP.FlagBitsCacheSize = S.FlagBitsCache.size();
+  CP.AssignEnumCacheSize = S.AssignEnumCache.size();
+  CP.WeakUndeclaredIdentifiersSize = S.WeakUndeclaredIdentifiers.size();
+  CP.ExtnameUndeclaredIdentifiersSize = S.ExtnameUndeclaredIdentifiers.size();
+  CP.UnusedLocalTypedefNameCandidatesSize =
+      S.UnusedLocalTypedefNameCandidates.size();
+  CP.UnusedFileScopedDeclsSize = S.UnusedFileScopedDecls.end();
+  CP.TentativeDefinitionsSize = S.TentativeDefinitions.end();
+  CP.ExternalDeclarationsSize = S.ExternalDeclarations.size();
+  CP.ParsingInitForAutoVarsSize = S.ParsingInitForAutoVars.size();
+  CP.DeclsToCheckForDeferredDiagsSize = S.DeclsToCheckForDeferredDiags.size();
+  CP.ShadowingDeclsSize = S.ShadowingDecls.size();
+  CP.WeakTopLevelDeclSize = S.WeakTopLevelDecl.size();
+  CP.ExtVectorDeclsSize = S.ExtVectorDecls.end();
+  CP.VTableUsesSize = S.VTableUses.size();
+  CP.VTablesUsedSize = S.VTablesUsed.size();
+  CP.DelayedDllExportClassesSize = S.DelayedDllExportClasses.size();
+  CP.DelayedDllExportMemberFunctionsSize =
+      S.DelayedDllExportMemberFunctions.size();
+  CP.InventedParameterInfosSize = S.InventedParameterInfos.size();
+  // CP.FieldCollectorSize = S.FieldCollector.size();
+  CP.UnusedPrivateFieldsSize = S.UnusedPrivateFields.size();
+  if (S.PureVirtualClassDiagSet)
+    CP.PureVirtualClassDiagSetSize = S.PureVirtualClassDiagSet->size();
+  CP.DelegatingCtorDeclsSize = S.DelegatingCtorDecls.end();
+  // CP.StdNamespaceSize = S.StdNamespace.size();
+  CP.UnparsedDefaultArgLocsSize = S.UnparsedDefaultArgLocs.size();
+  CP.UndefinedButUsedSize = S.UndefinedButUsed.size();
+  CP.SpecialMembersBeingDeclaredSize = S.SpecialMembersBeingDeclared.size();
+  CP.DelayedOverridingExceptionSpecChecksSize =
+      S.DelayedOverridingExceptionSpecChecks.size();
+  CP.DelayedEquivalentExceptionSpecChecksSize =
+      S.DelayedEquivalentExceptionSpecChecks.size();
+  CP.MaybeODRUseExprsSize = S.MaybeODRUseExprs.size();
+  CP.RefsMinusAssignmentsSize = S.RefsMinusAssignments.size();
+  CP.ExprCleanupObjectsSize = S.ExprCleanupObjects.size();
+  CP.ExprEvalContextsSize = S.ExprEvalContexts.size();
+  CP.FailedImmediateInvocationsSize = S.FailedImmediateInvocations.size();
+  // CP.ImplicitlyRetainedSelfLocsSize = S.ImplicitlyRetainedSelfLocs.size();
+  CP.DeleteExprsSize = S.DeleteExprs.size();
+  CP.CurrentParameterCopyTypesSize = S.CurrentParameterCopyTypes.size();
+  CP.AggregateDeductionCandidatesSize = S.AggregateDeductionCandidates.size();
+  CP.TypoCorrectionFailuresSize = S.TypoCorrectionFailures.size();
+  CP.SpecialMemberCacheSize = S.SpecialMemberCache.size();
+  CP.ModuleScopesSize = S.ModuleScopes.size();
+  CP.DeferredExportedNamespacesSize = S.DeferredExportedNamespaces.size();
+  CP.PendingInlineFuncDeclsSize = S.PendingInlineFuncDecls.size();
+  CP.CurrentSEHFinallySize = S.CurrentSEHFinally.size();
+  CP.CurrentDeferSize = S.CurrentDefer.size();
+  CP.LateParsedTemplateMapSize = S.LateParsedTemplateMap.size();
+  CP.SuppressedDiagnosticsSize = S.SuppressedDiagnostics.size();
+  // CP.CurrentInstantiationScopeSize = S.CurrentInstantiationScope.size();
+  CP.UnparsedDefaultArgInstantiationsSize =
+      S.UnparsedDefaultArgInstantiations.size();
+  CP.CodeSynthesisContextsSize = S.CodeSynthesisContexts.size();
+  CP.InstantiatingSpecializationsSize = S.InstantiatingSpecializations.size();
+  CP.InstantiatedNonDependentTypesSize = S.InstantiatedNonDependentTypes.size();
+  CP.CodeSynthesisContextLookupModulesSize =
+      S.CodeSynthesisContextLookupModules.size();
+  CP.LookupModulesCacheSize = S.LookupModulesCache.size();
+  CP.VisibleNamespaceCacheSize = S.VisibleNamespaceCache.size();
+  CP.TemplateInstCallbacksSize = S.TemplateInstCallbacks.size();
+  CP.PendingInstantiationsSize = S.PendingInstantiations.size();
+  CP.LateParsedInstantiationsSize = S.LateParsedInstantiations.size();
+  CP.SavedVTableUsesSize = S.SavedVTableUses.size();
+  CP.SavedPendingInstantiationsSize = S.SavedPendingInstantiations.size();
+  CP.PendingLocalImplicitInstantiationsSize =
+      S.PendingLocalImplicitInstantiations.size();
+  CP.UnsubstitutedConstraintSatisfactionCacheSize =
+      S.UnsubstitutedConstraintSatisfactionCache.size();
+  CP.SubsumptionCacheSize = S.SubsumptionCache.size();
+  CP.NormalizationCacheSize = S.NormalizationCache.size();
+  CP.SatisfactionCacheSize = S.SatisfactionCache.size();
+  CP.SatisfactionStackSize = S.SatisfactionStack.size();
+  // CP.NullabilityMapSize = S.NullabilityMap.size();
+  CP.DeclsWithEffectsToVerifySize = S.DeclsWithEffectsToVerify.size();
+  // CP.AllEffectsToVerifySize = S.AllEffectsToVerify.size();
+}
+
+void SemaStateStash::restore(SemaStashCheckPoint &CP,
+                             llvm::SlabCheckPoint SlabCP) {
+
+  ASTContext &Ctx = S.getASTContext();
+  if (CP.FunctionScopesSize != S.FunctionScopes.size()) {
+    llvm::dbgs() << "CP.FunctionScopesSize != S.FunctionScopes.size()\n";
+    S.FunctionScopes.resize(CP.FunctionScopesSize);
+    assert(CP.FunctionScopesSize == S.FunctionScopes.size());
+  }
+
+  if (CP.TypoCorrectedFunctionDefinitionsSize !=
+      S.TypoCorrectedFunctionDefinitions.size()) {
+    llvm::dbgs() << "CP.TypoCorrectedFunctionDefinitionsSize != "
+                    "S.TypoCorrectedFunctionDefinitions.size()\n";
+    // llvm::SmallPtrSet<const NamedDecl *, 4> TypoCorrectedFunctionDefinitions;
+    assert(CP.TypoCorrectedFunctionDefinitionsSize ==
+           S.TypoCorrectedFunctionDefinitions.size());
+  }
+
+  if (CP.FlagBitsCacheSize != S.FlagBitsCache.size()) {
+    llvm::dbgs() << "CP.FlagBitsCacheSize != S.FlagBitsCache.size()\n";
+    // mutable llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache;
+    assert(CP.FlagBitsCacheSize == S.FlagBitsCache.size());
+  }
+
+  if (CP.AssignEnumCacheSize != S.AssignEnumCache.size()) {
+    llvm::dbgs() << "CP.AssignEnumCacheSize != S.AssignEnumCache.size()\n";
+    //   llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt>>
+    //   AssignEnumCache;
+    assert(CP.AssignEnumCacheSize == S.AssignEnumCache.size());
+  }
+
+  if (CP.WeakUndeclaredIdentifiersSize != S.WeakUndeclaredIdentifiers.size()) {
+    llvm::dbgs() << "CP.WeakUndeclaredIdentifiersSize != "
+                    "S.WeakUndeclaredIdentifiers.size()\n";
+    //  llvm::MapVector<
+    //   IdentifierInfo *,
+    //   llvm::SetVector<
+    //       WeakInfo, llvm::SmallVector<WeakInfo, 1u>,
+    //       llvm::SmallDenseSet<WeakInfo, 2u,
+    //       WeakInfo::DenseMapInfoByAliasOnly>>>
+    //   WeakUndeclaredIdentifiers;
+    assert(CP.WeakUndeclaredIdentifiersSize ==
+           S.WeakUndeclaredIdentifiers.size());
+  }
+
+  if (CP.ExtnameUndeclaredIdentifiersSize !=
+      S.ExtnameUndeclaredIdentifiers.size()) {
+    llvm::dbgs() << "CP.ExtnameUndeclaredIdentifiersSize != "
+                    "S.ExtnameUndeclaredIdentifiers.size()\n";
+    //   llvm::DenseMap<IdentifierInfo *, AsmLabelAttr *>
+    //   ExtnameUndeclaredIdentifiers;
+    assert(CP.ExtnameUndeclaredIdentifiersSize ==
+           S.ExtnameUndeclaredIdentifiers.size());
+  }
+
+  if (CP.UnusedLocalTypedefNameCandidatesSize !=
+      S.UnusedLocalTypedefNameCandidates.size()) {
+    llvm::dbgs() << "CP.UnusedLocalTypedefNameCandidatesSize != "
+                    "S.UnusedLocalTypedefNameCandidates.size()\n";
+    //   llvm::SmallSetVector<const TypedefNameDecl *, 4>
+    //   UnusedLocalTypedefNameCandidates;
+    assert(CP.UnusedLocalTypedefNameCandidatesSize ==
+           S.UnusedLocalTypedefNameCandidates.size());
+  }
+
+  if (CP.UnusedFileScopedDeclsSize != S.UnusedFileScopedDecls.end()) {
+    llvm::dbgs()
+        << "CP.UnusedFileScopedDeclsSize != S.UnusedFileScopedDecls.end()\n";
+    //  typedef LazyVector<const DeclaratorDecl *, ExternalSemaSource,
+    //  &ExternalSemaSource::ReadUnusedFileScopedDecls, 2, 2>
+    //  UnusedFileScopedDeclsType;
+    assert(CP.UnusedFileScopedDeclsSize == S.UnusedFileScopedDecls.end());
+  }
+
+  if (CP.TentativeDefinitionsSize != S.TentativeDefinitions.end()) {
+    llvm::dbgs()
+        << "CP.TentativeDefinitionsSize != S.TentativeDefinitions.size()\n";
+    //    typedef LazyVector<VarDecl *, ExternalSemaSource,
+    //                  &ExternalSemaSource::ReadTentativeDefinitions, 2, 2>
+    //   TentativeDefinitionsType;
+    assert(CP.TentativeDefinitionsSize == S.TentativeDefinitions.end());
+  }
+
+  if (CP.ExternalDeclarationsSize != S.ExternalDeclarations.size()) {
+    llvm::dbgs()
+        << "CP.ExternalDeclarationsSize != S.ExternalDeclarations.size()\n";
+    //    SmallVector<DeclaratorDecl *, 4> ExternalDeclarations;
+    assert(CP.ExternalDeclarationsSize == S.ExternalDeclarations.size());
+  }
+
+  if (CP.ParsingInitForAutoVarsSize != S.ParsingInitForAutoVars.size()) {
+    llvm::dbgs()
+        << "CP.ParsingInitForAutoVarsSize != S.ParsingInitForAutoVars.size()\n";
+    //   llvm::SmallPtrSet<const Decl *, 4> ParsingInitForAutoVars;
+    assert(CP.ParsingInitForAutoVarsSize == S.ParsingInitForAutoVars.size());
+  }
+
+  if (CP.DeclsToCheckForDeferredDiagsSize !=
+      S.DeclsToCheckForDeferredDiags.size()) {
+    llvm::dbgs() << "CP.DeclsToCheckForDeferredDiagsSize != "
+                    "S.DeclsToCheckForDeferredDiags.size()\n";
+    //   llvm::SmallSetVector<Decl *, 4> DeclsToCheckForDeferredDiags;
+    assert(CP.DeclsToCheckForDeferredDiagsSize ==
+           S.DeclsToCheckForDeferredDiags.size());
+  }
+
+  if (CP.ShadowingDeclsSize != S.ShadowingDecls.size()) {
+    llvm::dbgs() << "CP.ShadowingDeclsSize != S.ShadowingDecls.size()\n";
+    //   llvm::DenseMap<const NamedDecl *, const NamedDecl *> ShadowingDecls;
+    assert(CP.ShadowingDeclsSize == S.ShadowingDecls.size());
+  }
+
+  if (CP.WeakTopLevelDeclSize != S.WeakTopLevelDecl.size()) {
+    llvm::dbgs() << "CP.WeakTopLevelDeclSize != S.WeakTopLevelDecl.size()\n";
+    //    SmallVector<Decl *, 2> WeakTopLevelDecl;
+    assert(CP.WeakTopLevelDeclSize == S.WeakTopLevelDecl.size());
+  }
+
+  if (CP.ExtVectorDeclsSize != S.ExtVectorDecls.end()) {
+    llvm::dbgs() << "CP.ExtVectorDeclsSize != S.ExtVectorDecls.size()\n";
+    //   typedef LazyVector<TypedefNameDecl *, ExternalSemaSource,
+    //  &ExternalSemaSource::ReadExtVectorDecls, 2, 2> ExtVectorDecls;
+    assert(CP.ExtVectorDeclsSize == S.ExtVectorDecls.end());
+  }
+
+  if (CP.VTableUsesSize != S.VTableUses.size()) {
+    llvm::dbgs() << "CP.VTableUsesSize != S.VTableUses.size()\n";
+    //   SmallVector<VTableUse, 16> VTableUses;
+    assert(CP.VTableUsesSize == S.VTableUses.size());
+  }
+
+  if (CP.VTablesUsedSize != S.VTablesUsed.size()) {
+    llvm::dbgs() << "CP.VTablesUsedSize != S.VTablesUsed.size()\n";
+    //   llvm::DenseMap<CXXRecordDecl *, bool> VTablesUsed;
+    assert(CP.VTablesUsedSize == S.VTablesUsed.size());
+  }
+
+  if (CP.DelayedDllExportClassesSize != S.DelayedDllExportClasses.size()) {
+    llvm::dbgs() << "CP.DelayedDllExportClassesSize != "
+                    "S.DelayedDllExportClasses.size()\n";
+    //   SmallVector<CXXRecordDecl *, 4> DelayedDllExportClasses;
+    assert(CP.DelayedDllExportClassesSize == S.DelayedDllExportClasses.size());
+  }
+
+  if (CP.DelayedDllExportMemberFunctionsSize !=
+      S.DelayedDllExportMemberFunctions.size()) {
+    llvm::dbgs() << "CP.DelayedDllExportMemberFunctionsSize != "
+                    "S.DelayedDllExportMemberFunctions.size()\n";
+    //   SmallVector<CXXMethodDecl *, 4> DelayedDllExportMemberFunctions;
+    assert(CP.DelayedDllExportMemberFunctionsSize ==
+           S.DelayedDllExportMemberFunctions.size());
+  }
+
+  if (CP.InventedParameterInfosSize != S.InventedParameterInfos.size()) {
+    llvm::dbgs()
+        << "CP.InventedParameterInfosSize != S.InventedParameterInfos.size()\n";
+    //   SmallVector<InventedTemplateParameterInfo, 4> InventedParameterInfos;
+    assert(CP.InventedParameterInfosSize == S.InventedParameterInfos.size());
+  }
+
+  if (CP.UnusedPrivateFieldsSize != S.UnusedPrivateFields.size()) {
+    llvm::dbgs()
+        << "CP.UnusedPrivateFieldsSize != S.UnusedPrivateFields.size()\n";
+    //   typedef llvm::SmallSetVector<const NamedDecl *, 16> NamedDeclSetType;
+    /// Set containing all declared private fields that are not used.
+    //   NamedDeclSetType UnusedPrivateFields;
+    assert(CP.UnusedPrivateFieldsSize == S.UnusedPrivateFields.size());
+  }
+
+  if (S.PureVirtualClassDiagSet &&
+      (CP.PureVirtualClassDiagSetSize != S.PureVirtualClassDiagSet->size())) {
+    llvm::dbgs() << "CP.PureVirtualClassDiagSetSize != "
+                    "S.PureVirtualClassDiagSet.size()\n";
+    //   typedef llvm::SmallPtrSet<const CXXRecordDecl *, 8> RecordDeclSetTy;
+
+    /// PureVirtualClassDiagSet - a set of class declarations which we have
+    /// emitted a list of pure virtual functions. Used to prevent emitting the
+    /// same list more than once.
+    //   std::unique_ptr<RecordDeclSetTy> PureVirtualClassDiagSet;
+    assert(CP.PureVirtualClassDiagSetSize == S.PureVirtualClassDiagSet->size());
+  }
+
+  if (CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.end()) {
+    llvm::dbgs()
+        << "CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.size()\n";
+    //   typedef LazyVector<CXXConstructorDecl *, ExternalSemaSource,
+    //  &ExternalSemaSource::ReadDelegatingConstructors, 2, 2>
+    //   DelegatingCtorDeclsType;
+
+    /// All the delegating constructors seen so far in the file, used for
+    /// cycle detection at the end of the TU.
+    //   DelegatingCtorDeclsType DelegatingCtorDecls;
+
+    assert(CP.DelegatingCtorDeclsSize == S.DelegatingCtorDecls.end());
+  }
+
+  if (CP.UnparsedDefaultArgLocsSize != S.UnparsedDefaultArgLocs.size()) {
+    llvm::dbgs()
+        << "CP.UnparsedDefaultArgLocsSize != S.UnparsedDefaultArgLocs.size()\n";
+    //   llvm::DenseMap<ParmVarDecl *, SourceLocation> UnparsedDefaultArgLocs;
+    assert(CP.UnparsedDefaultArgLocsSize == S.UnparsedDefaultArgLocs.size());
+  }
+
+  if (CP.UndefinedButUsedSize != S.UndefinedButUsed.size()) {
+    llvm::dbgs() << "CP.UndefinedButUsedSize != S.UndefinedButUsed.size()\n";
+      // llvm::MapVector<NamedDecl *, SourceLocation> UndefinedButUsed;
+    while (CP.UndefinedButUsedSize != S.UndefinedButUsed.size())
+      S.UndefinedButUsed.pop_back();
+    assert(CP.UndefinedButUsedSize == S.UndefinedButUsed.size());
+  }
+
+  if (CP.SpecialMembersBeingDeclaredSize !=
+      S.SpecialMembersBeingDeclared.size()) {
+    llvm::dbgs() << "CP.SpecialMembersBeingDeclaredSize != "
+                    "S.SpecialMembersBeingDeclared.size()\n";
+    //   llvm::SmallPtrSet<SpecialMemberDecl, 4> SpecialMembersBeingDeclared;
+    assert(CP.SpecialMembersBeingDeclaredSize ==
+           S.SpecialMembersBeingDeclared.size());
+  }
+
+  if (CP.SpecialMembersBeingDeclaredSize !=
+      S.SpecialMembersBeingDeclared.size()) {
+    llvm::dbgs() << "CP.SpecialMembersBeingDeclaredSize != "
+                    "S.SpecialMembersBeingDeclared.size()\n";
+    //   llvm::SmallPtrSet<SpecialMemberDecl, 4> SpecialMembersBeingDeclared;
+    assert(CP.SpecialMembersBeingDeclaredSize ==
+           S.SpecialMembersBeingDeclared.size());
+  }
+
+  if (CP.DelayedOverridingExceptionSpecChecksSize !=
+      S.DelayedOverridingExceptionSpecChecks.size()) {
+    llvm::dbgs() << "CP.DelayedOverridingExceptionSpecChecksSize != "
+                    "S.DelayedOverridingExceptionSpecChecks.size()\n";
+    //     SmallVector<std::pair<const CXXMethodDecl *, const CXXMethodDecl *>,
+    //     2>
+    //   DelayedOverridingExceptionSpecChecks;
+    assert(CP.DelayedOverridingExceptionSpecChecksSize ==
+           S.DelayedOverridingExceptionSpecChecks.size());
+  }
+
+  if (CP.DelayedEquivalentExceptionSpecChecksSize !=
+      S.DelayedEquivalentExceptionSpecChecks.size()) {
+    llvm::dbgs() << "CP.DelayedEquivalentExceptionSpecChecksSize != "
+                    "S.DelayedEquivalentExceptionSpecChecks.size()\n";
+    //   SmallVector<std::pair<FunctionDecl *, FunctionDecl *>, 2>
+    //   DelayedEquivalentExceptionSpecChecks;
+    assert(CP.DelayedEquivalentExceptionSpecChecksSize ==
+           S.DelayedEquivalentExceptionSpecChecks.size());
+  }
+
+  if (CP.MaybeODRUseExprsSize != S.MaybeODRUseExprs.size()) {
+    llvm::dbgs() << "CP.MaybeODRUseExprsSize != S.MaybeODRUseExprs.size()\n";
+    // S.MaybeODRUseExprs.resize(CP.MaybeODRUseExprsSize);
+    assert(CP.MaybeODRUseExprsSize == S.MaybeODRUseExprs.size());
+  }
+
+  if (CP.RefsMinusAssignmentsSize != S.RefsMinusAssignments.size()) {
+    llvm::dbgs()
+        << "CP.RefsMinusAssignmentsSize != S.RefsMinusAssignments.size()\n";
+    //     llvm::DenseMap<const VarDecl *, int> RefsMinusAssignments;
+    eraseDenseMapIf(
+        S.RefsMinusAssignments,
+        [&](llvm::detail::DenseMapPair<const VarDecl *, int> &KV) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+              static_cast<void *>(const_cast<VarDecl *>(KV.getFirst())),
+              SlabCP);
+        });
+    assert(CP.RefsMinusAssignmentsSize == S.RefsMinusAssignments.size());
+  }
+
+  if (CP.ExprCleanupObjectsSize != S.ExprCleanupObjects.size()) {
+    llvm::dbgs()
+        << "CP.ExprCleanupObjectsSize != S.ExprCleanupObjects.size()\n";
+    //   SmallVector<ExprWithCleanups::CleanupObject, 8> ExprCleanupObjects;
+    assert(CP.ExprCleanupObjectsSize == S.ExprCleanupObjects.size());
+  }
+
+  if (CP.ExprEvalContextsSize != S.ExprEvalContexts.size()) {
+    llvm::dbgs() << "CP.ExprEvalContextsSize != S.ExprEvalContexts.size()\n";
+    //   SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts;
+    assert(CP.ExprEvalContextsSize == S.ExprEvalContexts.size());
+  }
+
+  if (CP.FailedImmediateInvocationsSize !=
+      S.FailedImmediateInvocations.size()) {
+    llvm::dbgs() << "CP.FailedImmediateInvocationsSize != "
+                    "S.FailedImmediateInvocations.size()\n";
+    //    llvm::SmallPtrSet<ConstantExpr *, 4> FailedImmediateInvocations;
+    assert(CP.FailedImmediateInvocationsSize ==
+           S.FailedImmediateInvocations.size());
+  }
+
+  if (CP.DeleteExprsSize != S.DeleteExprs.size()) {
+    llvm::dbgs() << "CP.DeleteExprsSize != S.DeleteExprs.size()\n";
+    //   llvm::MapVector<FieldDecl *, DeleteLocs> DeleteExprs;
+    assert(CP.DeleteExprsSize == S.DeleteExprs.size());
+  }
+
+  if (CP.CurrentParameterCopyTypesSize != S.CurrentParameterCopyTypes.size()) {
+    llvm::dbgs() << "CP.CurrentParameterCopyTypesSize != "
+                    "S.CurrentParameterCopyTypes.size()\n";
+    //    llvm::SmallVector<QualType, 4> CurrentParameterCopyTypes;
+    assert(CP.CurrentParameterCopyTypesSize ==
+           S.CurrentParameterCopyTypes.size());
+  }
+
+  if (CP.AggregateDeductionCandidatesSize !=
+      S.AggregateDeductionCandidates.size()) {
+    llvm::dbgs() << "CP.AggregateDeductionCandidatesSize != "
+                    "S.AggregateDeductionCandidates.size()\n";
+    //   llvm::DenseMap<unsigned, CXXDeductionGuideDecl *>
+    //   AggregateDeductionCandidates;
+    assert(CP.AggregateDeductionCandidatesSize ==
+           S.AggregateDeductionCandidates.size());
+  }
+
+  if (CP.TypoCorrectionFailuresSize != S.TypoCorrectionFailures.size()) {
+    llvm::dbgs() << "CP.TypoCorrectionFailuresSize != "
+                    "S.TypoCorrectionFailures.size()\n";
+    // eraseDenseMapIf(
+    //     S.TypoCorrectionFailures,
+    //     [&](llvm::detail::DenseMapPair<IdentifierInfo *, Sema::SrcLocSet> &KV)
+    //         -> bool {
+    //           llvm::outs() << "SlabCheckPoint Cur : " << (void *)SlabCP.CurPtr << "\n";
+    //           llvm::outs() << "SlabCheckPoint END : " << (void *)SlabCP.End << "\n";
+    //           llvm::outs() << "Addr: " << static_cast<void *>(KV.getFirst()) << "\n";
+    //       return Ctx.getAllocator().isAfterCheckpoint(
+    //           static_cast<void *>(KV.getFirst()), SlabCP);
+    //     });
+    // assert(CP.TypoCorrectionFailuresSize == S.TypoCorrectionFailures.size());
+  }
+
+  if (CP.ModuleScopesSize != S.ModuleScopes.size()) {
+    llvm::dbgs() << "CP.ModuleScopesSize != "
+                    "S.ModuleScopes.size()\n";
+    //  llvm::SmallVector<ModuleScope, 16> ModuleScopes;
+    assert(CP.ModuleScopesSize == S.ModuleScopes.size());
+  }
+
+  if (CP.DeferredExportedNamespacesSize !=
+      S.DeferredExportedNamespaces.size()) {
+    llvm::dbgs() << "CP.DeferredExportedNamespacesSize != "
+                    "S.DeferredExportedNamespaces.size()\n";
+    /// Namespace definitions that we will export when they finish.
+    // llvm::SmallPtrSet<const NamespaceDecl *, 8> DeferredExportedNamespaces;
+    assert(CP.DeferredExportedNamespacesSize ==
+           S.DeferredExportedNamespaces.size());
+  }
+
+  if (CP.PendingInlineFuncDeclsSize != S.PendingInlineFuncDecls.size()) {
+    llvm::dbgs() << "CP.PendingInlineFuncDeclsSize != "
+                    "S.PendingInlineFuncDecls.size()\n";
+    ///   /// In a C++ standard module, inline declarations require a definition
+    ///   to be
+    /// present at the end of a definition domain.  This set holds the decls to
+    /// be checked at the end of the TU.
+    //   llvm::SmallPtrSet<const FunctionDecl *, 8> PendingInlineFuncDecls;
+
+    assert(CP.PendingInlineFuncDeclsSize == S.PendingInlineFuncDecls.size());
+  }
+
+  if (CP.CurrentSEHFinallySize != S.CurrentSEHFinally.size()) {
+    llvm::dbgs() << "CP.CurrentSEHFinallySize != "
+                    "S.CurrentSEHFinally.size()\n";
+    //    /// Stack of active SEH __finally scopes.  Can be empty.
+    // SmallVector<Scope *, 2> CurrentSEHFinally;
+
+    assert(CP.CurrentSEHFinallySize == S.CurrentSEHFinally.size());
+  }
+
+  if (CP.CurrentDeferSize != S.CurrentDefer.size()) {
+    llvm::dbgs() << "CP.CurrentDeferSize != "
+                    "S.CurrentDefer.size()\n";
+    //    /// Stack of '_Defer' statements that are currently being parsed, as
+    //    well
+    /// as the locations of their '_Defer' keywords. Can be empty.
+    //   SmallVector<std::pair<Scope *, SourceLocation>, 2> CurrentDefer;
+
+    assert(CP.CurrentDeferSize == S.CurrentDefer.size());
+  }
+
+  if (CP.LateParsedTemplateMapSize != S.LateParsedTemplateMap.size()) {
+    llvm::dbgs() << "CP.LateParsedTemplateMapSize != "
+                    "S.LateParsedTemplateMap.size()\n";
+    //   typedef llvm::MapVector<const FunctionDecl *,
+    //                           std::unique_ptr<LateParsedTemplate>>
+    //       LateParsedTemplateMapT;
+    //   LateParsedTemplateMapT LateParsedTemplateMap;
+    assert(CP.LateParsedTemplateMapSize == S.LateParsedTemplateMap.size());
+  }
+
+  if (CP.SuppressedDiagnosticsSize != S.SuppressedDiagnostics.size()) {
+    llvm::dbgs() << "CP.SuppressedDiagnosticsSize != "
+                    "S.SuppressedDiagnostics.size()\n";
+    //    typedef llvm::DenseMap<Decl *, SmallVector<PartialDiagnosticAt, 1>>
+    //       SuppressedDiagnosticsMap;
+    //   SuppressedDiagnosticsMap SuppressedDiagnostics;
+    assert(CP.SuppressedDiagnosticsSize == S.SuppressedDiagnostics.size());
+  }
+
+  if (CP.UnparsedDefaultArgInstantiationsSize !=
+      S.UnparsedDefaultArgInstantiations.size()) {
+    llvm::dbgs() << "CP.UnparsedDefaultArgInstantiationsSize != "
+                    "S.UnparsedDefaultArgInstantiations.size()\n";
+
+    //   typedef llvm::DenseMap<ParmVarDecl *, llvm::TinyPtrVector<ParmVarDecl
+    //   *>>
+    //       UnparsedDefaultArgInstantiationsMap;
+
+    //   /// A mapping from parameters with unparsed default arguments to the
+    //   /// set of instantiations of each parameter.
+    //   ///
+    //   /// This mapping is a temporary data structure used when parsing
+    //   /// nested class templates or nested classes of class templates,
+    //   /// where we might end up instantiating an inner class before the
+    //   /// default arguments of its methods have been parsed.
+    //   UnparsedDefaultArgInstantiationsMap UnparsedDefaultArgInstantiations;
+
+    assert(CP.UnparsedDefaultArgInstantiationsSize ==
+           S.UnparsedDefaultArgInstantiations.size());
+  }
+
+  if (CP.CodeSynthesisContextsSize != S.CodeSynthesisContexts.size()) {
+    llvm::dbgs() << "CP.CodeSynthesisContextsSize != "
+                    "S.CodeSynthesisContexts.size()\n";
+    //  SmallVector<CodeSynthesisContext, 16> CodeSynthesisContexts;
+
+    assert(CP.CodeSynthesisContextsSize == S.CodeSynthesisContexts.size());
+  }
+
+  if (CP.InstantiatingSpecializationsSize !=
+      S.InstantiatingSpecializations.size()) {
+    llvm::dbgs() << "CP.InstantiatingSpecializationsSize != "
+                    "S.InstantiatingSpecializations.size()\n";
+    //    /// Specializations whose definitions are currently being
+    //    instantiated.
+    //   llvm::DenseSet<InstantiatingSpecializationsKey>
+    //   InstantiatingSpecializations;
+
+    assert(CP.InstantiatingSpecializationsSize ==
+           S.InstantiatingSpecializations.size());
+  }
+
+  if (CP.InstantiatedNonDependentTypesSize !=
+      S.InstantiatedNonDependentTypes.size()) {
+    llvm::dbgs() << "CP.InstantiatedNonDependentTypesSize != "
+                    "S.InstantiatedNonDependentTypes.size()\n";
+    //      llvm::DenseSet<QualType> InstantiatedNonDependentTypes;
+    assert(CP.InstantiatedNonDependentTypesSize ==
+           S.InstantiatedNonDependentTypes.size());
+  }
+
+  if (CP.CodeSynthesisContextLookupModulesSize !=
+      S.CodeSynthesisContextLookupModules.size()) {
+    llvm::dbgs() << "CP.CodeSynthesisContextLookupModulesSize != "
+                    "S.CodeSynthesisContextLookupModules.size()\n";
+    //      SmallVector<Module *, 16> CodeSynthesisContextLookupModules;
+
+    assert(CP.CodeSynthesisContextLookupModulesSize ==
+           S.CodeSynthesisContextLookupModules.size());
+  }
+
+  if (CP.LookupModulesCacheSize != S.LookupModulesCache.size()) {
+    llvm::dbgs() << "CP.LookupModulesCacheSize != "
+                    "S.LookupModulesCache.size()\n";
+    //        llvm::DenseSet<Module *> LookupModulesCache;
+
+    assert(CP.LookupModulesCacheSize == S.LookupModulesCache.size());
+  }
+
+  if (CP.VisibleNamespaceCacheSize != S.VisibleNamespaceCache.size()) {
+    llvm::dbgs() << "CP.VisibleNamespaceCacheSize != "
+                    "S.VisibleNamespaceCache.size()\n";
+    //           llvm::DenseMap<NamedDecl *, NamedDecl *> VisibleNamespaceCache;
+
+    assert(CP.VisibleNamespaceCacheSize == S.VisibleNamespaceCache.size());
+  }
+
+  if (CP.TemplateInstCallbacksSize != S.TemplateInstCallbacks.size()) {
+    llvm::dbgs() << "CP.TemplateInstCallbacksSize != "
+                    "S.TemplateInstCallbacks.size()\n";
+    //    std::vector<std::unique_ptr<TemplateInstantiationCallback>>
+    //   TemplateInstCallbacks;
+    assert(CP.TemplateInstCallbacksSize == S.TemplateInstCallbacks.size());
+  }
+
+  if (CP.PendingInstantiationsSize != S.PendingInstantiations.size()) {
+    llvm::dbgs() << "CP.PendingInstantiationsSize != "
+                    "S.PendingInstantiations.size()\n";
+    //      std::deque<PendingImplicitInstantiation> PendingInstantiations;
+
+    assert(CP.PendingInstantiationsSize == S.PendingInstantiations.size());
+  }
+
+  if (CP.LateParsedInstantiationsSize != S.LateParsedInstantiations.size()) {
+    llvm::dbgs() << "CP.LateParsedInstantiationsSize != "
+                    "S.LateParsedInstantiations.size()\n";
+    S.LateParsedInstantiations.resize(CP.LateParsedInstantiationsSize);
+    assert(CP.LateParsedInstantiationsSize ==
+           S.LateParsedInstantiations.size());
+  }
+
+  if (CP.SavedVTableUsesSize != S.SavedVTableUses.size()) {
+    llvm::dbgs() << "CP.SavedVTableUsesSize != "
+                    "S.SavedVTableUses.size()\n";
+    S.SavedVTableUses.resize(CP.SavedVTableUsesSize);
+    assert(CP.SavedVTableUsesSize == S.SavedVTableUses.size());
+  }
+
+  if (CP.SavedPendingInstantiationsSize !=
+      S.SavedPendingInstantiations.size()) {
+    llvm::dbgs() << "CP.SavedPendingInstantiationsSize != "
+                    "S.SavedPendingInstantiations.size()\n";
+    S.SavedPendingInstantiations.resize(CP.SavedPendingInstantiationsSize);
+    assert(CP.SavedPendingInstantiationsSize ==
+           S.SavedPendingInstantiations.size());
+  }
+
+  if (CP.PendingLocalImplicitInstantiationsSize !=
+      S.PendingLocalImplicitInstantiations.size()) {
+    llvm::dbgs() << "CP.PendingLocalImplicitInstantiationsSize != "
+                    "S.PendingLocalImplicitInstantiations.size()\n";
+    S.PendingLocalImplicitInstantiations.resize(
+        CP.PendingLocalImplicitInstantiationsSize);
+    assert(CP.PendingLocalImplicitInstantiationsSize ==
+           S.PendingLocalImplicitInstantiations.size());
+  }
+
+  if (CP.UnsubstitutedConstraintSatisfactionCacheSize !=
+      S.UnsubstitutedConstraintSatisfactionCache.size()) {
+    llvm::dbgs() << "CP.UnsubstitutedConstraintSatisfactionCacheSize != "
+                    "S.UnsubstitutedConstraintSatisfactionCache.size()\n";
+    // eraseDenseMapIf(
+    //     S.UnsubstitutedConstraintSatisfactionCache,
+    //     [&](UnsubstitutedConstraintSatisfactionCacheResult &R) -> bool {
+    //       return Ctx.isAfterCheckpoint(static_cast<void
+    //       *>(R.SubstExpr.get()),
+    //                                    SlabCP)
+    //     });
+    assert(CP.UnsubstitutedConstraintSatisfactionCacheSize ==
+           S.UnsubstitutedConstraintSatisfactionCache.size());
+  }
+
+  if (CP.SubsumptionCacheSize != S.SubsumptionCache.size()) {
+    llvm::dbgs() << "CP.SubsumptionCacheSize != "
+                    "S.SubsumptionCache.size()\n";
+    // eraseDenseMapIf(
+    //     S.SubsumptionCache,
+    //     [&](llvm::DenseMap<std::pair<const NamedDecl *, const NamedDecl *>,
+    //                        bool>::key_value &KV) -> bool {
+    //       return Ctx.isAfterCheckpoint(static_cast<void *>(KV.first.first),
+    //                                    SlabCP) ||
+    //              Ctx.isAfterCheckpoint(static_cast<void *>(KV.first.second),
+    //                                    SlabCP);
+    //     });
+    assert(CP.SubsumptionCacheSize == S.SubsumptionCache.size());
+  }
+
+  if (CP.NormalizationCacheSize != S.NormalizationCache.size()) {
+    llvm::dbgs() << "CP.NormalizationCacheSize != "
+                    "S.NormalizationCache.size()\n";
+    //   llvm::DenseMap<ConstrainedDeclOrNestedRequirement, NormalizedConstraint
+    //   *> NormalizationCache;
+    assert(CP.NormalizationCacheSize == S.NormalizationCache.size());
+  }
+
+  if (CP.SatisfactionCacheSize != S.SatisfactionCache.size()) {
+    llvm::dbgs() << "CP.SatisfactionCacheSize != "
+                    "S.SatisfactionCache.size()\n";
+    //   llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &>
+    //   SatisfactionCache;
+    assert(CP.SatisfactionCacheSize == S.SatisfactionCache.size());
+  }
+
+  if (CP.SatisfactionStackSize != S.SatisfactionStack.size()) {
+    llvm::dbgs() << "CP.SatisfactionStackSize != "
+                    "S.SatisfactionStack.size()\n";
+    //         llvm::SmallVector<SatisfactionStackEntryTy, 10>
+    //         SatisfactionStack;
+    assert(CP.SatisfactionStackSize == S.SatisfactionStack.size());
+  }
+
+  // if (CP.NullabilityMapSize != S.NullabilityMap.size()) {
+  //   llvm::dbgs() << "CP.NullabilityMapSize != "
+  //                   "S.NullabilityMap.size()\n";
+  //   //  FileNullabilityMap NullabilityMap;;
+  //   assert(CP.NullabilityMapSize == S.NullabilityMap.size());
+  // }
+
+  if (CP.DeclsWithEffectsToVerifySize != S.DeclsWithEffectsToVerify.size()) {
+    llvm::dbgs() << "CP.DeclsWithEffectsToVerifySize != "
+                    "S.DeclsWithEffectsToVerify.size()\n";
+    //  SmallVector<const Decl *> DeclsWithEffectsToVerify;;
+    assert(CP.DeclsWithEffectsToVerifySize ==
+           S.DeclsWithEffectsToVerify.size());
+  }
+
+  if (CP.SpecialMemberCacheSize != S.SpecialMemberCache.size()) {
+    llvm::dbgs() << "CP.SpecialMemberCacheSize != "
+                    "S.SpecialMemberCache.size()\n";
+    // eraseFoldingSetIf(S.SpecialMemberCache,
+    //                   [&](Sema::SpecialMemberOverloadResultEntry &Node) ->
+    //                   bool {
+    //                     return S.BumpAlloc.isAfterCheckpoint(
+    //                         static_cast<void *>(&Node), CP.SemaBumpSlabCP);
+    //                   });
+    assert(CP.SpecialMemberCacheSize == S.SpecialMemberCache.size());
+  }
+
+  std::vector<NamedDecl *> ToRemoved;
+  for (auto *TmpD : S.getCurScope()->decls()) {
+    assert(TmpD && "This decl didn't get pushed??");
+
+    assert(isa<NamedDecl>(TmpD) && "Decl isn't NamedDecl?");
+    NamedDecl *D = cast<NamedDecl>(TmpD);
+
+    if (!D->getDeclName()) continue;
+
+    if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D), SlabCP)) {
+      if (D->getDeclName().getFETokenInfo())
+        S.IdResolver.RemoveDecl(D);
+      ToRemoved.push_back(D);
+    }
+  }
+
+  for (auto ND : ToRemoved)
+    S.getCurScope()->RemoveDecl(ND);
+
+  S.BumpAlloc.restoreToCheckPoint(CP.SemaBumpSlabCP);
+}
+
+} // namespace clang
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index 79e2040fcd381..3709972ea99d8 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -23,6 +23,7 @@
 #include "llvm/Support/AllocatorBase.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
@@ -41,6 +42,13 @@ LLVM_ABI void printBumpPtrAllocatorStats(unsigned NumSlabs, size_t TotalMemory);
 
 } // end namespace detail
 
+struct SlabCheckPoint {
+  unsigned ActiveSlabIdx;
+  char *CurPtr;
+  char *End;
+  size_t BytesAllocated;
+};
+
 /// Allocate memory in an ever growing pool, as if by bump-pointer.
 ///
 /// This isn't strictly a bump-pointer allocator as it uses backing slabs of
@@ -209,8 +217,16 @@ class BumpPtrAllocatorImpl
       return AlignedPtr;
     }
 
-    // Otherwise, start a new slab and try again.
-    StartNewSlab();
+    if (!Slabs.empty() && Slabs.size() > 1 &&
+        (ActiveSlabIdx < Slabs.size() - 1)) {
+      ActiveSlabIdx++;
+      void *NewSlab = Slabs[ActiveSlabIdx];
+      size_t AllocatedSlabSize = computeSlabSize(ActiveSlabIdx);
+      CurPtr = (char *)(NewSlab);
+      End = ((char *)NewSlab) + AllocatedSlabSize;
+    } else
+      // Otherwise, start a new slab and try again.
+      StartNewSlab();
     uintptr_t AlignedAddr = alignAddr(CurPtr, Alignment);
     assert(AlignedAddr + SizeToAllocate < EndSentinel &&
            "Unable to allocate memory!");
@@ -242,6 +258,61 @@ class BumpPtrAllocatorImpl
 
   size_t GetNumSlabs() const { return Slabs.size() + CustomSizedSlabs.size(); }
 
+  SlabCheckPoint checkPoint() const {
+    return {ActiveSlabIdx, CurPtr, End, BytesAllocated};
+  }
+
+  static void poisonMemory(void *Ptr, size_t Size) {
+#if LLVM_ADDRESS_SANITIZER_BUILD
+    __asan_poison_memory_region(Ptr, Size);
+#else
+    // In non-ASAN builds, overwrite with a known poison pattern
+    // so use-after-rewind crashes deterministically in debug builds
+// #ifndef NDEBUG
+    memset(Ptr, 0xCD, Size); // 0xCD = classic "dead memory" pattern
+// #endif
+#endif
+  }
+
+  bool isAfterCheckpoint(const void *Ptr, const SlabCheckPoint &CP) const {
+    const char *P = static_cast<const char *>(Ptr);
+
+    // Check active slab — past the checkpoint CurPtr
+    if (CP.ActiveSlabIdx < Slabs.size()) {
+      const char *Start = static_cast<const char *>(Slabs[CP.ActiveSlabIdx]);
+      if (P >= CP.CurPtr && P < (Start + computeSlabSize(CP.ActiveSlabIdx)))
+        return true;
+    }
+
+    // Check slabs allocated entirely after checkpoint
+    for (unsigned I = CP.ActiveSlabIdx + 1; I < Slabs.size(); ++I) {
+      const char *Start = static_cast<const char *>(Slabs[I]);
+      const char *End = Start + computeSlabSize(I);
+      if (P >= Start && P < End)
+        return true;
+    }
+
+    return false;
+  }
+
+  void restoreToCheckPoint(SlabCheckPoint CP) {
+    assert(CP.ActiveSlabIdx >= 0 && CP.ActiveSlabIdx < Slabs.size());
+    assert(CP.CurPtr >= (const char *)Slabs[CP.ActiveSlabIdx] &&
+           CP.End == ((const char *)Slabs[CP.ActiveSlabIdx] +
+                      computeSlabSize(CP.ActiveSlabIdx)));
+    ActiveSlabIdx = CP.ActiveSlabIdx;
+    CurPtr = CP.CurPtr;
+    End = CP.End;
+    BytesAllocated = CP.BytesAllocated;
+    llvm::outs() << "Poisoned range = [" << (void *)CurPtr << ", " << (void *)End << ")\n";
+    llvm::outs() << "Poisoned End Size = [" << (void *)CurPtr << ", " << (void *)(CurPtr + (size_t)(End - CurPtr)) << ")\n";
+    llvm::outs().flush();
+    poisonMemory((void *)CurPtr, (size_t)(End - CurPtr));
+    for (unsigned I = ActiveSlabIdx + 1; I < Slabs.size(); ++I)
+      // Should we deallocate any extra slabs?
+      poisonMemory(Slabs[I], computeSlabSize(I));
+  }
+
   /// \return An index uniquely and reproducibly identifying
   /// an input pointer \p Ptr in the given allocator.
   /// The returned value is negative iff the object is inside a custom-size
@@ -325,6 +396,8 @@ class BumpPtrAllocatorImpl
   /// path condition also rejects a empty allocator with a 0-size allocation.
   uintptr_t EndSentinel = 0;
 
+  unsigned ActiveSlabIdx = 0;
+
   /// The slabs allocated so far.
   SmallVector<void *, 4> Slabs;
 
@@ -349,7 +422,8 @@ class BumpPtrAllocatorImpl
   /// Allocate a new slab and move the bump pointers over into the new
   /// slab, modifying CurPtr and EndSentinel.
   void StartNewSlab() {
-    size_t AllocatedSlabSize = computeSlabSize(Slabs.size());
+    ActiveSlabIdx = Slabs.size();
+    size_t AllocatedSlabSize = computeSlabSize(ActiveSlabIdx);
 
     void *NewSlab = this->getAllocator().Allocate(AllocatedSlabSize,
                                                   alignof(std::max_align_t));

>From fd752aabad6ac6a4fdf7e2a4635298d60bbe1595 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 26 Jul 2026 12:37:31 +0530
Subject: [PATCH 02/13] Add fixes for crashes found during testing

---
 clang/include/clang/AST/DeclBase.h            |   2 +
 clang/include/clang/AST/RecordLayout.h        |   2 +
 .../include/clang/Interpreter/ErrorRecovery.h |  23 +-
 .../lib/Interpreter/ASTContextStateStash.cpp  | 285 +++++++++++++-----
 clang/lib/Interpreter/IncrementalAction.cpp   |  12 +-
 clang/lib/Interpreter/IncrementalAction.h     |   3 +-
 clang/lib/Interpreter/IncrementalParser.cpp   |   1 +
 clang/lib/Interpreter/Interpreter.cpp         |   4 +-
 clang/lib/Interpreter/SemaStateStash.cpp      |  66 ++--
 .../ptu-adl-namespace-collisions.cpp          |  97 ++++++
 .../test/Interpreter/ptu-class-edge-cases.cpp | 105 +++++++
 .../Interpreter/ptu-class-sema-recovery.cpp   | 112 +++++++
 .../test/Interpreter/ptu-lookup-recovery.cpp  |  49 +++
 clang/test/Interpreter/ptu-rewind-stress.cpp  | 259 ++++++++++++++++
 14 files changed, 914 insertions(+), 106 deletions(-)
 create mode 100644 clang/test/Interpreter/ptu-adl-namespace-collisions.cpp
 create mode 100644 clang/test/Interpreter/ptu-class-edge-cases.cpp
 create mode 100644 clang/test/Interpreter/ptu-class-sema-recovery.cpp
 create mode 100644 clang/test/Interpreter/ptu-lookup-recovery.cpp
 create mode 100644 clang/test/Interpreter/ptu-rewind-stress.cpp

diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 39082a8973b2b..9ec6f4c84a857 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -1345,9 +1345,11 @@ namespace llvm {
 }
 
 namespace clang {
+class ASTContextStateStash;
 /// A list storing NamedDecls in the lookup tables.
 class DeclListNode {
   friend class ASTContext; // allocate, deallocate nodes.
+  friend class ASTContextStateStash;
   friend class StoredDeclsList;
 public:
   using Decls = llvm::PointerUnion<NamedDecl*, DeclListNode*>;
diff --git a/clang/include/clang/AST/RecordLayout.h b/clang/include/clang/AST/RecordLayout.h
index 9af17df229b37..73ecb90fbcd88 100644
--- a/clang/include/clang/AST/RecordLayout.h
+++ b/clang/include/clang/AST/RecordLayout.h
@@ -26,6 +26,7 @@
 namespace clang {
 
 class ASTContext;
+class ASTContextStateStash;
 class CXXRecordDecl;
 
 /// ASTRecordLayout -
@@ -60,6 +61,7 @@ class ASTRecordLayout {
 
 private:
   friend class ASTContext;
+  friend class ASTContextStateStash;
 
   /// Size - Size of record in characters.
   CharUnits Size;
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 6d5605a9624e6..245d9416a8933 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -188,8 +188,6 @@ struct StashCheckPoint {
   size_t TypedefTypesSize = 0;
   size_t DependentNameTypesSize = 0;
   size_t PackExpansionTypesSize = 0;
-  size_t ObjCObjectTypesSize = 0;
-  size_t ObjCObjectPointerTypesSize = 0;
   size_t UnaryTransformTypesSize = 0;
 
   size_t AutoTypesSize = 0;
@@ -248,6 +246,7 @@ struct StashCheckPoint {
   size_t RequireVectorDeletingDtorSize = 0;
 
   size_t MergedDeclsSize = 0;
+  size_t DeclAttrsSize = 0;
   size_t MergedDefModulesSize = 0;
 
   size_t ModuleInitializersSize = 0;
@@ -277,8 +276,25 @@ struct StashCheckPoint {
   size_t ExtraMangleNumberingContextsSize = 0;
 
   size_t TraversalScopeSize = 0;
-  llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
+
+  /// object-c
+  size_t ObjCObjectTypesSize = 0;
+  size_t ObjCObjectPointerTypesSize = 0;
+
+  mutable TypedefDecl *ObjCIdDeclCP = nullptr;
+
+  mutable TypedefDecl *ObjCSelDeclCP = nullptr;
+
+  mutable TypedefDecl *ObjCClassDeclCP = nullptr;
+
+  mutable ObjCInterfaceDecl *ObjCProtocolClassDeclCP = nullptr;
+
+  // llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
 //    = llvm::PointerIntPair<StoredDeclsMap *, 1>(nullptr, 0);
+  mutable QualType AutoDeductTy;     // Deduction against 'auto'.
+  mutable QualType AutoRRefDeductTy; // Deduction against 'auto &&'.
+
+  // mutable DeclarationNameTable DeclarationNames; need to revert.
 };
 
 class ASTContextStateStash {
@@ -295,6 +311,5 @@ class ASTContextStateStash {
   void restore(StashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
   void commit();
 };
-
 } // end namespace clang
 #endif // LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
diff --git a/clang/lib/Interpreter/ASTContextStateStash.cpp b/clang/lib/Interpreter/ASTContextStateStash.cpp
index 18ad727ddc5ef..e25c67c46aa01 100644
--- a/clang/lib/Interpreter/ASTContextStateStash.cpp
+++ b/clang/lib/Interpreter/ASTContextStateStash.cpp
@@ -11,11 +11,13 @@
 
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
-#include "clang/AST/DeclContextInternals.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclContextInternals.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/MangleNumberingContext.h"
+#include "clang/AST/RecordLayout.h"
 #include "clang/AST/Type.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
@@ -30,7 +32,6 @@ namespace clang {
 template <typename EntryType, typename PredT>
 static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
   SmallVector<EntryType *, 16> ToRemove;
-
   for (auto &N : FS)
     if (Pred(N))
       ToRemove.push_back(&N);
@@ -57,7 +58,6 @@ eraseFoldingSetIf(llvm::ContextualFoldingSet<EntryType, ASTContext &> &FS,
 template <typename KeyT, typename ValueT, typename PredT>
 static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
   SmallVector<KeyT, 16> ToRemove;
-
   for (auto &KV : Map)
     if (Pred(KV))
       ToRemove.push_back(KV.getFirst());
@@ -191,6 +191,7 @@ void ASTContextStateStash::stash(StashCheckPoint &CP) {
   CP.RequireVectorDeletingDtorSize = Ctx.RequireVectorDeletingDtor.size();
 
   CP.MergedDeclsSize = Ctx.MergedDecls.size();
+  CP.DeclAttrsSize = Ctx.DeclAttrs.size();
   CP.MergedDefModulesSize = Ctx.MergedDefModules.size();
 
   CP.ModuleInitializersSize = Ctx.ModuleInitializers.size();
@@ -222,11 +223,33 @@ void ASTContextStateStash::stash(StashCheckPoint &CP) {
   CP.MangleNumberingContextsSize = Ctx.MangleNumberingContexts.size();
   CP.ExtraMangleNumberingContextsSize = Ctx.ExtraMangleNumberingContexts.size();
   CP.TraversalScopeSize = Ctx.TraversalScope.size();
-  CP.LastSDM = Ctx.LastSDM;
+  // CP.LastSDM = Ctx.LastSDM;
+  CP.AutoDeductTy = Ctx.AutoDeductTy;         // Deduction against 'auto'.
+  CP.AutoRRefDeductTy = Ctx.AutoRRefDeductTy; // Deduction against 'auto &&'.
+
+  /// ObjectC
+  CP.ObjCObjectTypesSize = Ctx.ObjCObjectTypes.size();
+  CP.ObjCObjectPointerTypesSize = Ctx.ObjCObjectPointerTypes.size();
+  CP.ObjCIdDeclCP = Ctx.ObjCIdDecl;
+
+  CP.ObjCSelDeclCP = Ctx.ObjCSelDecl;
+
+  CP.ObjCClassDeclCP = Ctx.ObjCClassDecl;
+
+  Ctx.ObjCProtocolClassDecl = CP.ObjCProtocolClassDeclCP;
 }
 
 void ASTContextStateStash::restore(StashCheckPoint &CP,
                                    llvm::SlabCheckPoint SlabCP) {
+  if (CP.AutoDeductTy != Ctx.AutoDeductTy) {
+    llvm::dbgs() << "Ctx.AutoDeductTy != CP.AutoDeductTy\n";
+    Ctx.AutoDeductTy = CP.AutoDeductTy;
+  }
+
+  if (CP.AutoRRefDeductTy != Ctx.AutoRRefDeductTy) {
+    llvm::dbgs() << "Ctx.AutoRRefDeductTy != CP.AutoRRefDeductTy\n";
+    Ctx.AutoRRefDeductTy = CP.AutoRRefDeductTy;
+  }
 
   if (Ctx.TemplateTypeParmTypes.size() != CP.TemplateTypeParmTypesSize) {
     llvm::dbgs() << "Ctx.TemplateTypeParmTypes.size() != "
@@ -864,18 +887,22 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()) {
     llvm::dbgs()
         << "if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()\n";
+    std::vector<const ASTRecordLayout *> ToBeDestroyed;
     eraseDenseMapIf(
         Ctx.ASTRecordLayouts,
         [&](llvm::detail::DenseMapPair<const RecordDecl *,
                                        const ASTRecordLayout *> &KV) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-                     static_cast<void *>(
-                         const_cast<RecordDecl *>(KV.getFirst())),
-                     SlabCP) ||
-                 Ctx.getAllocator().isAfterCheckpoint(
-                     static_cast<void *>(
-                         const_cast<ASTRecordLayout *>(KV.getSecond())),
-                     SlabCP);
+          bool IsAfter =
+              Ctx.getAllocator().isAfterCheckpoint(
+                  static_cast<void *>(const_cast<RecordDecl *>(KV.getFirst())),
+                  SlabCP) ||
+              Ctx.getAllocator().isAfterCheckpoint(
+                  static_cast<void *>(
+                      const_cast<ASTRecordLayout *>(KV.getSecond())),
+                  SlabCP);
+          if (IsAfter)
+            const_cast<ASTRecordLayout *>(KV.getSecond())->Destroy(Ctx);
+          return IsAfter;
         });
     assert(CP.ASTRecordLayoutsSize == Ctx.ASTRecordLayouts.size());
   }
@@ -922,7 +949,7 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
               static_cast<void *>(const_cast<CXXRecordDecl *>(KV.getFirst())),
               SlabCP);
         });
-    assert(CP.KeyFunctionsSize == Ctx.KeyFunctions.size());
+    // assert(CP.KeyFunctionsSize == Ctx.KeyFunctions.size());
   }
 
   if (CP.BlockVarCopyInitsSize != Ctx.BlockVarCopyInits.size()) {
@@ -1022,8 +1049,8 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
                  Ctx.getAllocator().isAfterCheckpoint(
                      static_cast<void *>(KV.getSecond()), SlabCP);
         });
-    assert(CP.OperatorDeletesForVirtualDtorSize ==
-           Ctx.OperatorDeletesForVirtualDtor.size());
+    // assert(CP.OperatorDeletesForVirtualDtorSize ==
+    //        Ctx.OperatorDeletesForVirtualDtor.size());
   }
 
   if (CP.GlobalOperatorDeletesForVirtualDtorSize !=
@@ -1097,91 +1124,106 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   }
 
   if (CP.MergedDeclsSize != Ctx.MergedDecls.size()) {
-    llvm::dbgs() << "CP.MergedDeclsSize != Ctx.MergedDecls.size()";
+    llvm::dbgs() << "CP.MergedDeclsSize != Ctx.MergedDecls.size()\n";
     assert(CP.MergedDeclsSize == Ctx.MergedDecls.size());
   }
 
+  if (CP.DeclAttrsSize != Ctx.DeclAttrs.size()) {
+    llvm::dbgs() << "CP.DeclAttrsSize != Ctx.DeclAttrs.size()\n";
+    eraseDenseMapIf(
+        Ctx.DeclAttrs,
+        [&](llvm::detail::DenseMapPair<const Decl *, AttrVec *> &KV) -> bool {
+          bool isAfter =
+              Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP) ||
+              Ctx.getAllocator().isAfterCheckpoint(KV.getSecond(), SlabCP);
+          if (isAfter)
+            KV.getSecond()->~AttrVec();
+          return isAfter;
+        });
+    assert(CP.DeclAttrsSize == Ctx.DeclAttrs.size());
+  }
+
   if (CP.MergedDefModulesSize != Ctx.MergedDefModules.size()) {
-    llvm::dbgs() << "CP.MergedDefModulesSize != Ctx.MergedDefModules.size()";
+    llvm::dbgs() << "CP.MergedDefModulesSize != Ctx.MergedDefModules.size()\n";
     assert(CP.MergedDefModulesSize == Ctx.MergedDefModules.size());
   }
 
   if (CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()) {
     llvm::dbgs()
-        << "CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()";
+        << "CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()\n";
     assert(CP.ModuleInitializersSize == Ctx.ModuleInitializers.size());
   }
 
   if (CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()) {
     llvm::dbgs()
-        << "CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()";
+        << "CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()\n";
     assert(CP.PrimaryModuleNameMapSize == Ctx.PrimaryModuleNameMap.size());
   }
 
   if (CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()) {
     llvm::dbgs()
-        << "CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()";
+        << "CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()\n";
     assert(CP.SameModuleLookupSetSize == Ctx.SameModuleLookupSet.size());
   }
 
   if (CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()) {
-    llvm::dbgs() << "CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()";
+    llvm::dbgs() << "CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()\n";
     assert(CP.ScalableVecTyMapSize == Ctx.ScalableVecTyMap.size());
   }
 
   if (CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()) {
-    llvm::dbgs() << "CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()";
+    llvm::dbgs() << "CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()\n";
     assert(CP.LambdaCastPathsSize == Ctx.LambdaCastPaths.size());
   }
 
   if (CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()) {
-    llvm::dbgs() << "CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()";
+    llvm::dbgs() << "CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()\n";
     assert(CP.DeclRawCommentsSize == Ctx.DeclRawComments.size());
   }
 
   if (CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()) {
     llvm::dbgs()
-        << "CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()";
+        << "CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()\n";
     assert(CP.RedeclChainCommentsSize == Ctx.RedeclChainComments.size());
   }
 
   if (CP.CommentlessRedeclChainsSize != Ctx.CommentlessRedeclChains.size()) {
     llvm::dbgs() << "CP.CommentlessRedeclChainsSize != "
-                    "Ctx.CommentlessRedeclChains.size()";
+                    "Ctx.CommentlessRedeclChains.size()\n";
     assert(CP.CommentlessRedeclChainsSize ==
            Ctx.CommentlessRedeclChains.size());
   }
 
   if (CP.ParsedCommentsSize != Ctx.ParsedComments.size()) {
-    llvm::dbgs() << "CP.ParsedCommentsSize != Ctx.ParsedComments.size()";
+    llvm::dbgs() << "CP.ParsedCommentsSize != Ctx.ParsedComments.size()\n";
     assert(CP.ParsedCommentsSize == Ctx.ParsedComments.size());
   }
 
   if (CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()) {
     llvm::dbgs()
-        << "CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()";
+        << "CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()\n";
     assert(CP.RelocatableClassesSize == Ctx.RelocatableClasses.size());
   }
 
   if (CP.ParamIndicesSize != Ctx.ParamIndices.size()) {
-    llvm::dbgs() << "CP.ParamIndicesSize != Ctx.ParamIndices.size()";
+    llvm::dbgs() << "CP.ParamIndicesSize != Ctx.ParamIndices.size()\n";
     assert(CP.ParamIndicesSize == Ctx.ParamIndices.size());
   }
 
   if (CP.MangleNumbersSize != Ctx.MangleNumbers.size()) {
-    llvm::dbgs() << "CP.MangleNumbersSize != Ctx.MangleNumbers.size()";
+    llvm::dbgs() << "CP.MangleNumbersSize != Ctx.MangleNumbers.size()\n";
     assert(CP.MangleNumbersSize == Ctx.MangleNumbers.size());
   }
 
   if (CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()) {
     llvm::dbgs()
-        << "CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()";
+        << "CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()\n";
     assert(CP.StaticLocalNumbersSize == Ctx.StaticLocalNumbers.size());
   }
 
   if (CP.TemplateOrInstantiationSize != Ctx.TemplateOrInstantiation.size()) {
     llvm::dbgs() << "CP.TemplateOrInstantiationSize != "
-                    "Ctx.TemplateOrInstantiation.size()";
+                    "Ctx.TemplateOrInstantiation.size()\n";
     assert(CP.TemplateOrInstantiationSize ==
            Ctx.TemplateOrInstantiation.size());
   }
@@ -1189,7 +1231,7 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   if (CP.InstantiatedFromUsingDeclSize !=
       Ctx.InstantiatedFromUsingDecl.size()) {
     llvm::dbgs() << "CP.InstantiatedFromUsingDeclSize != "
-                    "Ctx.InstantiatedFromUsingDecl.size()";
+                    "Ctx.InstantiatedFromUsingDecl.size()\n";
     assert(CP.InstantiatedFromUsingDeclSize ==
            Ctx.InstantiatedFromUsingDecl.size());
   }
@@ -1197,7 +1239,7 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   if (CP.InstantiatedFromUsingEnumDeclSize !=
       Ctx.InstantiatedFromUsingEnumDecl.size()) {
     llvm::dbgs() << "CP.InstantiatedFromUsingEnumDeclSize != "
-                    "Ctx.InstantiatedFromUsingEnumDecl.size()";
+                    "Ctx.InstantiatedFromUsingEnumDecl.size()\n";
     assert(CP.InstantiatedFromUsingEnumDeclSize ==
            Ctx.InstantiatedFromUsingEnumDecl.size());
   }
@@ -1205,7 +1247,7 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   if (CP.InstantiatedFromUsingShadowDeclSize !=
       Ctx.InstantiatedFromUsingShadowDecl.size()) {
     llvm::dbgs() << "CP.InstantiatedFromUsingShadowDeclSize != "
-                    "Ctx.InstantiatedFromUsingShadowDecl.size()";
+                    "Ctx.InstantiatedFromUsingShadowDecl.size()\n";
     assert(CP.InstantiatedFromUsingShadowDeclSize ==
            Ctx.InstantiatedFromUsingShadowDecl.size());
   }
@@ -1213,19 +1255,72 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   if (CP.InstantiatedFromUnnamedFieldDeclSize !=
       Ctx.InstantiatedFromUnnamedFieldDecl.size()) {
     llvm::dbgs() << "CP.InstantiatedFromUnnamedFieldDeclSize != "
-                    "Ctx.InstantiatedFromUnnamedFieldDecl.size()";
+                    "Ctx.InstantiatedFromUnnamedFieldDecl.size()\n";
     assert(CP.InstantiatedFromUnnamedFieldDeclSize ==
            Ctx.InstantiatedFromUnnamedFieldDecl.size());
   }
 
   if (CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()) {
-    llvm::dbgs() << "CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()";
+    llvm::dbgs()
+        << "CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()\n";
+    // using CXXMethodVector = llvm::TinyPtrVector<const CXXMethodDecl *>;
+    // llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector> OverriddenMethods;
+    // eraseDenseMapIf(
+    //     Ctx.OverriddenMethods,
+    //     [&](llvm::detail::DenseMapPair<const CXXMethodDecl *,
+    //     CXXMethodVector>
+    //             &KV) -> bool {
+    //       bool isAfter = Ctx.getAllocater().isAfterCheckpoint(
+    //           static_cast<void *>(const_cast<CXXMethodDecl
+    //           *>(KV.getFirst())), SlabCP);
+    //       if (!isAfter) {
+    //         std::vector<CXXMethodDecl *> ToBeRemoved;
+    //         ToBeRemoved.reserve(KV.getSecond().size());
+    //         for (auto *M : std::reverse(KV.getSecond()))
+    //           if (Ctx.getAllocater().isAfterCheckpoint(
+    //                   static_cast<void *>(const_cast<CXXMethodDecl *>(M)),
+    //                   SlabCP)) {
+    //             ToBeRemoved.push_back(M);
+    //           }
+    //         for (auto *M : ToBeRemoved)
+    //           KV.getSecond().erase(M);
+    //       }
+    //       return isAfter;
+    //     });
+
+    for (auto &KV : Ctx.OverriddenMethods) {
+      if (Ctx.getAllocator().isAfterCheckpoint(KV.first, SlabCP))
+        continue; // entire entry will be removed in second pass
+
+      auto &Vec = KV.getSecond();
+      llvm::SmallVector<const CXXMethodDecl *, 4> ToRemove;
+      for (auto *M : Vec)
+        if (Ctx.getAllocator().isAfterCheckpoint(M, SlabCP))
+          ToRemove.push_back(M);
+
+      for (auto *M : ToRemove)
+        Vec.erase(llvm::find(Vec, M));
+    }
+
+    eraseDenseMapIf(
+        Ctx.OverriddenMethods,
+        [&](llvm::detail::DenseMapPair<
+            const CXXMethodDecl *, ASTContext::CXXMethodVector> &KV) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
+        });
     assert(CP.OverriddenMethodsSize == Ctx.OverriddenMethods.size());
   }
 
   if (CP.MangleNumberingContextsSize != Ctx.MangleNumberingContexts.size()) {
     llvm::dbgs() << "CP.MangleNumberingContextsSize != "
-                    "Ctx.MangleNumberingContexts.size()";
+                    "Ctx.MangleNumberingContexts.size()\n";
+    eraseDenseMapIf(
+        Ctx.MangleNumberingContexts,
+        [&](llvm::detail::DenseMapPair<
+            const DeclContext *, std::unique_ptr<MangleNumberingContext>> &KV)
+            -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
+        });
     assert(CP.MangleNumberingContextsSize ==
            Ctx.MangleNumberingContexts.size());
   }
@@ -1233,17 +1328,61 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   if (CP.ExtraMangleNumberingContextsSize !=
       Ctx.ExtraMangleNumberingContexts.size()) {
     llvm::dbgs() << "CP.ExtraMangleNumberingContextsSize != "
-                    "Ctx.ExtraMangleNumberingContexts.size()";
+                    "Ctx.ExtraMangleNumberingContexts.size()\n";
     assert(CP.ExtraMangleNumberingContextsSize ==
            Ctx.ExtraMangleNumberingContexts.size());
   }
 
   if (CP.TraversalScopeSize != Ctx.TraversalScope.size()) {
     llvm::dbgs() << "CP.TraversalScopeSize != "
-                    "Ctx.TraversalScope.size()";
+                    "Ctx.TraversalScope.size()\n";
     assert(CP.TraversalScopeSize == Ctx.TraversalScope.size());
   }
 
+
+  /// Obj-C
+  if (CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()) {
+    llvm::dbgs() << "CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()\n";
+    eraseFoldingSetIf(Ctx.ObjCObjectTypes,
+                      [&](ObjCObjectTypeImpl &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(CP.ObjCObjectTypesSize == Ctx.ObjCObjectTypes.size());
+  }
+
+  if (CP.ObjCObjectPointerTypesSize != Ctx.ObjCObjectPointerTypes.size()) {
+    llvm::dbgs() << "CP.ObjCObjectPointerTypesSize != "
+                    "Ctx.ObjCObjectPointerTypes.size()\n";
+    eraseFoldingSetIf(Ctx.ObjCObjectPointerTypes,
+                      [&](ObjCObjectPointerType &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(
+                            static_cast<void *>(&Node), SlabCP);
+                      });
+    assert(CP.ObjCObjectPointerTypesSize == Ctx.ObjCObjectPointerTypes.size());
+  }
+
+  if (CP.ObjCIdDeclCP != Ctx.ObjCIdDecl) {
+    llvm::dbgs() << "CP.ObjCIdDeclCP != Ctx.ObjCIdDecl\n";
+    Ctx.ObjCIdDecl = CP.ObjCIdDeclCP;
+    assert(CP.ObjCIdDeclCP == Ctx.ObjCIdDecl);
+  }
+  if (CP.ObjCSelDeclCP != Ctx.ObjCSelDecl) {
+    llvm::dbgs() << "CP.ObjCSelDeclCP != Ctx.ObjCSelDecl\n";
+    Ctx.ObjCSelDecl = CP.ObjCSelDeclCP;
+    assert(CP.ObjCSelDeclCP == Ctx.ObjCSelDecl);
+  }
+  if (CP.ObjCClassDeclCP != Ctx.ObjCClassDecl) {
+    llvm::dbgs() << "CP.ObjCClassDeclCP != Ctx.ObjCClassDecl\n";
+    Ctx.ObjCClassDecl = CP.ObjCClassDeclCP;
+    assert(CP.ObjCClassDeclCP == Ctx.ObjCClassDecl);
+  }
+  if (CP.ObjCProtocolClassDeclCP != Ctx.ObjCProtocolClassDecl) {
+    llvm::dbgs() << "CP.ObjCProtocolClassDeclCP != Ctx.ObjCProtocolClassDecl\n";
+    Ctx.ObjCProtocolClassDecl = CP.ObjCProtocolClassDeclCP;
+    assert(CP.ObjCProtocolClassDeclCP == Ctx.ObjCProtocolClassDecl);
+  }
+
   for (auto &[Decl, OldTy] : Ctx.PendingTypeForDeclMutations)
     Decl->TypeForDecl = OldTy;
 
@@ -1260,7 +1399,8 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
         std::vector<NamedDecl *> NamedDeclsToRemove;
         // bool RemoveAll = true;
         for (NamedDecl *D : R) {
-          // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU (" << (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
+          // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU (" <<
+          // (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
           // llvm::outs() << "DeclContext : " << DC << "\n";
           // D->dump();
           // if (D->getTranslationUnitDecl() == MostRecentTU)
@@ -1279,18 +1419,18 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
       }
     }
 
-    Decl *Prev = DC->FirstDecl;
-    Decl *Cur = DC->FirstDecl;
-    while (Cur) {
-      if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
-                                               SlabCP)) {
-        DC->LastDecl = Prev;
-        DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
-        break;
-      }
-      Prev = Cur;
-      Cur = Cur->getNextDeclInContext();
-    }
+    // Decl *Prev = DC->FirstDecl;
+    // Decl *Cur = DC->FirstDecl;
+    // while (Cur) {
+    //   if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
+    //                                            SlabCP)) {
+    //     DC->LastDecl = Prev;
+    //     DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
+    //     break;
+    //   }
+    //   Prev = Cur;
+    //   Cur = Cur->getNextDeclInContext();
+    // }
   }
 
   // if (FirstDecl) {
@@ -1305,11 +1445,9 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   // if (auto *Record = dyn_cast<CXXRecordDecl>(this))
   //   Record->addedMember(D);
 
-
   //   ImportDecl *FirstLocalImport = nullptr;
   // ImportDecl *LastLocalImport = nullptr;
 
-
   // class ImportDecl final : public Decl,
   //                        llvm::TrailingObjects<ImportDecl, SourceLocation> {
   // friend class ASTContext;
@@ -1323,7 +1461,8 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   // /// The next import in the list of imports local to the translation
   // /// unit being parsed (not loaded from an AST file).
   // ///
-  // /// Includes a bit that indicates whether we have source-location information
+  // /// Includes a bit that indicates whether we have source-location
+  // information
   // /// for each identifier in the module name.
   // ///
   // /// When the bit is false, we only have a single source location for the
@@ -1332,25 +1471,33 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
 
   Ctx.PendingDCMutations.clear();
 
-  llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM = Ctx.LastSDM;
+  // llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM = Ctx.LastSDM;
+
+  // StoredDeclsMap *Map = LastSDM.getPointer();
+  // bool Dependent = LastSDM.getInt();
+  // while (Map && (Map != CP.LastSDM.getPointer())) {
+  //   // llvm::outs() << "we are deleting LASTSDM\n";
+  // //   // Advance the iteration before we invalidate memory.
+  //   llvm::PointerIntPair<StoredDeclsMap*,1> Next = Map->Previous;
 
-  StoredDeclsMap *Map = LastSDM.getPointer();
-  bool Dependent = LastSDM.getInt();
-  while (Map && (Map != CP.LastSDM.getPointer())) {
-    // llvm::outs() << "we are deleting LASTSDM\n";
-  //   // Advance the iteration before we invalidate memory.
-    llvm::PointerIntPair<StoredDeclsMap*,1> Next = Map->Previous;
+  //   if (Dependent)
+  //     delete static_cast<DependentStoredDeclsMap*>(Map);
+  //   else
+  //     delete Map;
 
-    if (Dependent)
-      delete static_cast<DependentStoredDeclsMap*>(Map);
-    else
-      delete Map;
+  //   Map = Next.getPointer();
+  //   Dependent = Next.getInt();
+  // }
 
-    Map = Next.getPointer();
-    Dependent = Next.getInt();
+  // Ctx.LastSDM = CP.LastSDM;
+  DeclListNode *FreeNodeHead = Ctx.ListNodeFreeList;
+  while (FreeNodeHead) {
+    if (!Ctx.getAllocator().isAfterCheckpoint(FreeNodeHead, SlabCP))
+      break;
+    FreeNodeHead = dyn_cast_if_present<DeclListNode *>(FreeNodeHead->Rest);
   }
 
-  Ctx.LastSDM = CP.LastSDM;
+  Ctx.ListNodeFreeList = FreeNodeHead;
 
   Ctx.Types.resize(CP.TypesSize);
 }
diff --git a/clang/lib/Interpreter/IncrementalAction.cpp b/clang/lib/Interpreter/IncrementalAction.cpp
index 2c1ad404a3e7d..751cc7b58a034 100644
--- a/clang/lib/Interpreter/IncrementalAction.cpp
+++ b/clang/lib/Interpreter/IncrementalAction.cpp
@@ -54,7 +54,7 @@ IncrementalAction::IncrementalAction(CompilerInstance &Instance,
         }
         return Act;
       }()),
-      Interp(I), CI(Instance), Consumer(std::move(Consumer)) {}
+      Interp(I), CI(Instance), LLVMCtx(LLVMCtx), Consumer(std::move(Consumer)) {}
 
 std::unique_ptr<ASTConsumer>
 IncrementalAction::CreateASTConsumer(CompilerInstance & /*CI*/,
@@ -97,8 +97,10 @@ llvm::Module *IncrementalAction::getCachedCodeGenModule() const {
   return CachedInCodeGenModule.get();
 }
 
-std::unique_ptr<llvm::Module> IncrementalAction::GenModule() {
+std::unique_ptr<llvm::Module> IncrementalAction::GenModule(bool WasFailure) {
   static unsigned ID = 0;
+  if (WasFailure)
+    --ID;
   if (CodeGenerator *CG = getCodeGen()) {
     // Clang's CodeGen is designed to work with a single llvm::Module. In many
     // cases for convenience various CodeGen parts have a reference to the
@@ -115,8 +117,10 @@ std::unique_ptr<llvm::Module> IncrementalAction::GenModule() {
              CachedInCodeGenModule->alias_empty() &&
              CachedInCodeGenModule->ifunc_empty())) &&
            "CodeGen wrote to a readonly module");
-    std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
-    CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
+    std::unique_ptr<llvm::Module> M = nullptr;
+    if (!WasFailure)
+      M = CG->ReleaseModule();
+    CG->StartModule("incr_module_" + std::to_string(ID++), M ? M->getContext() : LLVMCtx);
     return M;
   }
   return nullptr;
diff --git a/clang/lib/Interpreter/IncrementalAction.h b/clang/lib/Interpreter/IncrementalAction.h
index 2893ff7b5baa6..13461dee7500f 100644
--- a/clang/lib/Interpreter/IncrementalAction.h
+++ b/clang/lib/Interpreter/IncrementalAction.h
@@ -36,6 +36,7 @@ class IncrementalAction : public WrapperFrontendAction {
   bool IsTerminating = false;
   Interpreter &Interp;
   [[maybe_unused]] CompilerInstance &CI;
+  llvm::LLVMContext &LLVMCtx;
   std::unique_ptr<ASTConsumer> Consumer;
 
   /// When CodeGen is created the first llvm::Module gets cached in many places
@@ -74,7 +75,7 @@ class IncrementalAction : public WrapperFrontendAction {
   CodeGenerator *getCodeGen() const;
 
   /// Generate an LLVM module for the most recent parsed input.
-  std::unique_ptr<llvm::Module> GenModule();
+  std::unique_ptr<llvm::Module> GenModule(bool WasFailure = false);
 };
 
 class InProcessPrintingASTConsumer final : public MultiplexConsumer {
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index bdd9d41b0ce48..6f913867ad159 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -88,6 +88,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
 
   DiagnosticsEngine &Diags = S.getDiagnostics();
   if (Diags.hasErrorOccurred()) {
+    Consumer->HandleTranslationUnit(C);
     CleanUpPTU(C.getTranslationUnitDecl());
 
     Diags.Reset(/*soft=*/true);
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index f951425c9c862..63961133b907d 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -627,8 +627,10 @@ Interpreter::Parse(llvm::StringRef Code) {
   PTUSlabRollback Rollback(CI->getSema());
 
   llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
-  if (!TuOrErr)
+  if (!TuOrErr) {
+    Act->GenModule(true);
     return TuOrErr.takeError();
+  }
 
   PartialTranslationUnit &LastPTU = IncrParser->RegisterPTU(*TuOrErr);
 
diff --git a/clang/lib/Interpreter/SemaStateStash.cpp b/clang/lib/Interpreter/SemaStateStash.cpp
index 8dce1182c4279..e72d0b7580bcb 100644
--- a/clang/lib/Interpreter/SemaStateStash.cpp
+++ b/clang/lib/Interpreter/SemaStateStash.cpp
@@ -28,16 +28,16 @@
 
 namespace clang {
 
-// template <typename EntryType, typename PredT>
-// static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred)
-// {
-//   SmallVector<EntryType *, 16> ToRemove;
-//   for (auto &N : FS)
-//     if (Pred(N))
-//       ToRemove.push_back(&N);
-//   for (auto *N : ToRemove)
-//     FS.RemoveNode(N);
-// }
+template <typename EntryType, typename PredT>
+static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred)
+{
+  SmallVector<EntryType *, 16> ToRemove;
+  for (auto &N : FS)
+    if (Pred(N))
+      ToRemove.push_back(&N);
+  for (auto *N : ToRemove)
+    FS.RemoveNode(N);
+}
 
 // template <typename EntryType, typename PredT>
 // static void eraseContextualFoldingSetIf(
@@ -74,15 +74,16 @@ static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
 //     Set.erase(Val);
 // }
 
-// template <typename T, typename PredT>
-// static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, 4> &Set, PredT &&Pred) {
-//   SmallVector<T, 8> ToRemove;
-//   for (T Val : Set)
-//     if (Pred(Val))
-//       ToRemove.push_back(Val);
-//   for (T Val : ToRemove)
-//     Set.erase(Val);
-// }
+template <typename T, unsigned SmallSize, typename PredT>
+static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, SmallSize> &Set,
+                               PredT &&Pred) {
+  SmallVector<T, 8> ToRemove;
+  for (T Val : Set)
+    if (Pred(Val))
+      ToRemove.push_back(Val);
+  for (T Val : ToRemove)
+    Set.erase(Val);
+}
 
 // template <typename T, unsigned N, typename PredT>
 // static void eraseSmallSetVectorIf(llvm::SmallSetVector<T, N> &SV,
@@ -382,7 +383,13 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
   if (CP.VTablesUsedSize != S.VTablesUsed.size()) {
     llvm::dbgs() << "CP.VTablesUsedSize != S.VTablesUsed.size()\n";
     //   llvm::DenseMap<CXXRecordDecl *, bool> VTablesUsed;
-    assert(CP.VTablesUsedSize == S.VTablesUsed.size());
+    eraseDenseMapIf(
+        S.VTablesUsed,
+        [&](llvm::detail::DenseMapPair<CXXRecordDecl *, bool> &KV) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(
+              static_cast<void *>(KV.getFirst()), SlabCP);
+        });
+    // assert(CP.VTablesUsedSize == S.VTablesUsed.size());
   }
 
   if (CP.DelayedDllExportClassesSize != S.DelayedDllExportClasses.size()) {
@@ -427,7 +434,11 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
     /// emitted a list of pure virtual functions. Used to prevent emitting the
     /// same list more than once.
     //   std::unique_ptr<RecordDeclSetTy> PureVirtualClassDiagSet;
-    assert(CP.PureVirtualClassDiagSetSize == S.PureVirtualClassDiagSet->size());
+    eraseSmallPtrSetIf(
+        *S.PureVirtualClassDiagSet.get(), [&](const CXXRecordDecl *RD) -> bool {
+          return Ctx.getAllocator().isAfterCheckpoint(RD, SlabCP);
+        });
+    // assert(CP.PureVirtualClassDiagSetSize == S.PureVirtualClassDiagSet->size());
   }
 
   if (CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.end()) {
@@ -848,12 +859,13 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
   if (CP.SpecialMemberCacheSize != S.SpecialMemberCache.size()) {
     llvm::dbgs() << "CP.SpecialMemberCacheSize != "
                     "S.SpecialMemberCache.size()\n";
-    // eraseFoldingSetIf(S.SpecialMemberCache,
-    //                   [&](Sema::SpecialMemberOverloadResultEntry &Node) ->
-    //                   bool {
-    //                     return S.BumpAlloc.isAfterCheckpoint(
-    //                         static_cast<void *>(&Node), CP.SemaBumpSlabCP);
-    //                   });
+    
+    eraseFoldingSetIf(
+        S.SpecialMemberCache,
+        [&](Sema::SpecialMemberOverloadResultEntry &Node) -> bool {
+          return S.BumpAlloc.isAfterCheckpoint(static_cast<void *>(&Node),
+                                               CP.SemaBumpSlabCP);
+        });
     assert(CP.SpecialMemberCacheSize == S.SpecialMemberCache.size());
   }
 
diff --git a/clang/test/Interpreter/ptu-adl-namespace-collisions.cpp b/clang/test/Interpreter/ptu-adl-namespace-collisions.cpp
new file mode 100644
index 0000000000000..4a86aea81cf5b
--- /dev/null
+++ b/clang/test/Interpreter/ptu-adl-namespace-collisions.cpp
@@ -0,0 +1,97 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl | FileCheck %s
+//
+// ADL / namespace / name-collision PTU rollback regression tests. As in
+// the class-focused file, each failing chunk pairs a genuine namespace- or
+// lookup-related Sema error (ambiguous lookup, hidden-friend/ADL misuse,
+// namespace-alias collision, conflicting using-declaration, ...) with a
+// legitimate new namespace member on the same line, so a correct rollback
+// must discard both together.
+
+extern "C" int printf(const char *, ...);
+
+// 1. Hidden friend found only via ADL, cf. SemaCXX/adl.cpp. combine() is
+// injected into its enclosing namespace but invisible to ordinary
+// unqualified lookup; calling it with non-class arguments is a genuine
+// "use of undeclared identifier" error, independent of any using-directive.
+namespace AdlNs1 { struct Widget1 { Widget1(int) {} friend int combine(Widget1, Widget1) { return 100; } }; }
+auto radl1 = printf("combine1=%d\n", combine(AdlNs1::Widget1(1), AdlNs1::Widget1(2)));
+// CHECK: combine1=100
+
+namespace AdlNs2 { struct Widget2 { Widget2(int) {} friend int combine(Widget2, Widget2) { return 200; } }; } int ambiguous_call = combine(1, 2);
+auto radl2 = printf("combine1_again=%d\n", combine(AdlNs1::Widget1(3), AdlNs1::Widget1(4)));
+// CHECK: combine1_again=100
+namespace AdlNs2 { struct Widget2 { Widget2(int) {} friend int combine(Widget2, Widget2) { return 200; } }; }
+auto radl3 = printf("combine2=%d\n", combine(AdlNs2::Widget2(5), AdlNs2::Widget2(6)));
+// CHECK: combine2=200
+
+// 2. Ambiguous unqualified lookup via using-directive collision, cf.
+// SemaCXX/using-directive.cpp (ambig_i).
+namespace NsX { int shared_val = 111; }
+namespace NsY { int other_val = 222; }
+using namespace NsX;
+auto rns1 = printf("shared_val=%d\n", shared_val);
+// CHECK: shared_val=111
+
+namespace NsZ { int shared_val = 333; int extra_z = 1; } using namespace NsZ; int ambiguous_ref = shared_val;
+auto rns2 = printf("shared_val=%d other_val=%d\n", shared_val, NsY::other_val);
+// CHECK: shared_val=111 other_val=222
+namespace NsZ2 { int shared_val = 444; }
+auto rns3 = printf("NsZ2_shared_val=%d\n", NsZ2::shared_val);
+// CHECK: NsZ2_shared_val=444
+
+// 3. Namespace-alias-vs-other-kind collision, cf. SemaCXX/namespace-alias.cpp.
+namespace RealNs { int val = 55; }
+namespace AliasA = RealNs;
+auto rns4 = printf("AliasA_val=%d\n", AliasA::val);
+// CHECK: AliasA_val=55
+
+namespace RealNs2 { int val2 = 66; } namespace AliasB = RealNs2; int CollideName; namespace CollideName = RealNs;
+namespace RealNs2 { int val2 = 77; }
+namespace AliasB = RealNs2;
+auto rns5 = printf("AliasB_val2=%d\n", AliasB::val2);
+// CHECK: AliasB_val2=77
+
+// 4. Inline namespace member lookup, cf.
+// SemaCXX/warn-inline-namespace-reopened-twice.cpp.
+namespace InlineOuter { inline namespace v1 { int inline_val = 9; } }
+auto rns6 = printf("inline_val=%d qualified=%d\n", InlineOuter::inline_val, InlineOuter::v1::inline_val);
+// CHECK: inline_val=9 qualified=9
+
+namespace InlineOuter { inline namespace v2 { int inline_val2 = 20; struct InlineWidget { int x; }; } } InlineOuter::InlineWidget bad_widget; bad_widget.y = 1;
+auto rns7 = printf("inline_val_again=%d\n", InlineOuter::inline_val);
+// CHECK: inline_val_again=9
+namespace InlineOuter { inline namespace v2 { int inline_val2 = 30; struct InlineWidget { int x; }; } }
+InlineOuter::InlineWidget good_widget; good_widget.x = 42;
+auto rns8 = printf("inline_val2=%d good_widget_x=%d\n", InlineOuter::inline_val2, good_widget.x);
+// CHECK: inline_val2=30 good_widget_x=42
+
+// 5. Conflicting using-declaration merging overload sets across
+// namespaces, cf. SemaCXX/using-decl.cpp, SemaCXX/using-decl-1.cpp.
+namespace FnNsA { int describe(int x) { return x + 1000; } }
+namespace FnNsB { int describe(double x) { return (int)(x + 2000); } }
+using FnNsA::describe; using FnNsB::describe;
+auto rns9 = printf("describe_int=%d describe_double=%d\n", describe(1), describe(1.0));
+// CHECK: describe_int=1001 describe_double=2001
+
+namespace FnNsC { int describe(int x) { return x + 3000; } int helper_c(int x) { return x * 2; } } using FnNsC::describe;
+auto rns10 = printf("describe_int2=%d describe_double2=%d\n", describe(2), describe(2.0));
+// CHECK: describe_int2=1002 describe_double2=2002
+namespace FnNsC { int helper_c(int x) { return x * 2; } }
+auto rns11 = printf("helper_c=%d\n", FnNsC::helper_c(5));
+// CHECK: helper_c=10
+
+// 6. Namespace reopened across chunks, interrupted by a failed reopening
+// that redefines an existing member with a conflicting type -- exercises
+// NamespaceDecl redeclaration-chain merging (Ctx.MergedDecls).
+namespace ReopenNs { int part1 = 1; }
+namespace ReopenNs { int part2 = 2; }
+auto rns12 = printf("part1=%d part2=%d\n", ReopenNs::part1, ReopenNs::part2);
+// CHECK: part1=1 part2=2
+
+namespace ReopenNs { int part3 = 3; double part1; }
+namespace ReopenNs { int part3 = 30; }
+auto rns13 = printf("part1=%d part2=%d part3=%d\n", ReopenNs::part1, ReopenNs::part2, ReopenNs::part3);
+// CHECK: part1=1 part2=2 part3=30
+
+%quit
diff --git a/clang/test/Interpreter/ptu-class-edge-cases.cpp b/clang/test/Interpreter/ptu-class-edge-cases.cpp
new file mode 100644
index 0000000000000..207ae20d26683
--- /dev/null
+++ b/clang/test/Interpreter/ptu-class-edge-cases.cpp
@@ -0,0 +1,105 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl | FileCheck %s
+//
+// Class-specific PTU rollback/recovery regression tests. Each scenario is
+// adapted from a real clang/test/SemaCXX case and reshaped into: a
+// committed baseline, a same-line chunk that mixes a legitimate new class
+// with a hard Sema error (forcing IncrementalParser to roll the whole
+// chunk back through PTUSlabRollback), then a follow-up chunk that
+// re-exercises the class-specific machinery the rolled-back chunk touched.
+
+extern "C" int printf(const char *, ...);
+
+// 1. Virtual inheritance / diamond layout, cf. SemaCXX/long-virtual-inheritance-chain.cpp.
+// Virtual bases force VTT and virtual-base-offset computation
+// (ASTRecordLayouts, OverriddenMethods, KeyFunctions) beyond what a
+// non-virtual hierarchy needs.
+struct VDiamondBase { virtual int who() { return 0; } virtual ~VDiamondBase() {} };
+struct VDiamondLeft : virtual VDiamondBase { int who() override { return 1; } };
+struct VDiamondRight : virtual VDiamondBase { int who() override { return 2; } };
+struct VDiamondJoin : VDiamondLeft, VDiamondRight { int who() override { return 3; } };
+VDiamondJoin vdj;
+auto rc1 = printf("vdj.who()=%d\n", vdj.who());
+// CHECK: vdj.who()=3
+
+struct VBad : virtual VDiamondBase { int who() override { return 9; } }; intentional_error_type vbad_garbage;
+VDiamondJoin vdj2;
+VDiamondBase* vbp = &vdj2;
+auto rc2 = printf("vbp->who()=%d\n", vbp->who());
+// CHECK: vbp->who()=3
+
+// 2. Abstract class instantiation, cf. SemaCXX/abstract.cpp. Uses the real
+// "allocating an object of abstract class type" diagnostic (rather than a
+// synthetic bad token) as the rollback trigger, alongside a legitimate
+// concrete subclass on the same line.
+struct AbstractShape { virtual double area() const = 0; virtual ~AbstractShape() {} };
+struct ConcreteSquare : AbstractShape { double side = 4.0; double area() const override { return side * side; } };
+ConcreteSquare sq1;
+auto rc3 = printf("sq1.area()=%.1f\n", sq1.area());
+// CHECK: sq1.area()=16.0
+
+struct ConcreteTriangle : AbstractShape { double base_len = 6.0, height = 2.0; double area() const override { return 0.5 * base_len * height; } }; AbstractShape *bad_instance = new AbstractShape();
+struct ConcreteTriangle : AbstractShape { double base_len = 6.0, height = 2.0; double area() const override { return 0.5 * base_len * height; } };
+ConcreteTriangle tri1;
+auto rc4 = printf("tri1.area()=%.1f\n", tri1.area());
+// CHECK: tri1.area()=6.0
+
+// 3. Anonymous union member lookup, cf. SemaCXX/anonymous-union.cpp.
+// Anonymous unions/structs synthesize IndirectFieldDecl chains that member
+// lookup has to see through.
+struct VariantBox { union { int as_int; float as_float; }; bool is_int; };
+VariantBox vb1; vb1.is_int = true; vb1.as_int = 55;
+auto rc5 = printf("vb1.as_int=%d\n", vb1.as_int);
+// CHECK: vb1.as_int=55
+
+struct VariantBoxBad { union { int as_int; struct { short lo; short hi; }; }; }; intentional_error_type variant_garbage;
+VariantBox vb2; vb2.is_int = false; vb2.as_float = 2.5f;
+auto rc6 = printf("vb2.as_float=%.1f is_int=%d\n", vb2.as_float, vb2.is_int);
+// CHECK: vb2.as_float=2.5 is_int=0
+
+// 4. Inheriting constructors / using-declarations, cf.
+// SemaCXX/cxx11-inheriting-ctors.cpp and SemaCXX/using-decl.cpp.
+// `using Base::Base;` synthesizes CXXConstructorDecls backed by
+// UsingShadowDecl bookkeeping distinct from ordinary member lookup.
+struct IctorBase { int val; IctorBase(int v) : val(v) {} };
+struct IctorDerived : IctorBase { using IctorBase::IctorBase; };
+IctorDerived id1(7);
+auto rc7 = printf("id1.val=%d\n", id1.val);
+// CHECK: id1.val=7
+
+struct IctorDerivedBad : IctorBase { using IctorBase::IctorBase; int extra = 0; }; intentional_error_type ictor_garbage;
+IctorDerived id2(13);
+auto rc8 = printf("id2.val=%d\n", id2.val);
+// CHECK: id2.val=13
+
+// 5. Bit-field record layout, cf. SemaCXX/bitfield.cpp. Bit-fields exercise
+// a layout code path (bit offsets within a storage unit) distinct from
+// plain field layout, cached alongside ASTRecordLayouts.
+struct Flags { unsigned a : 3; unsigned b : 5; unsigned c : 1; };
+Flags fl1; fl1.a = 5; fl1.b = 20; fl1.c = 1;
+auto rc9 = printf("fl1.a=%u fl1.b=%u fl1.c=%u sizeof=%lu\n", fl1.a, fl1.b, fl1.c, sizeof(Flags));
+// CHECK: fl1.a=5 fl1.b=20 fl1.c=1
+
+struct FlagsBad { unsigned x : 4; unsigned y : 30; }; intentional_error_type bitfield_garbage;
+Flags fl2; fl2.a = 7; fl2.b = 31; fl2.c = 0;
+auto rc10 = printf("fl2.a=%u fl2.b=%u fl2.c=%u sizeof=%lu\n", fl2.a, fl2.b, fl2.c, sizeof(Flags));
+// CHECK: fl2.a=7 fl2.b=31 fl2.c=0
+
+// 6. Pointer-to-member-function targeting a virtual method, cf.
+// SemaCXX/member-pointer.cpp. Dereferencing such a pointer needs the
+// vtable index of the target, tying MemberPointerType caching to the same
+// virtual-dispatch bookkeeping stressed in test 1.
+struct MPBase { virtual int compute(int x) { return x + 1; } virtual ~MPBase() {} };
+struct MPDerived : MPBase { int compute(int x) override { return x * 2; } };
+int (MPBase::*mp1)(int) = &MPBase::compute;
+MPDerived mpd1;
+auto rc11 = printf("(mpd1.*mp1)(5)=%d\n", (mpd1.*mp1)(5));
+// CHECK: (mpd1.*mp1)(5)=10
+
+struct MPDerivedBad : MPBase { int compute(int x) override { return x * 3; } }; int (MPBase::*mp_bad)(int) = &MPBase::compute; intentional_error_type mp_garbage;
+MPDerived mpd2;
+int (MPBase::*mp2)(int) = &MPBase::compute;
+auto rc12 = printf("(mpd2.*mp2)(5)=%d\n", (mpd2.*mp2)(5));
+// CHECK: (mpd2.*mp2)(5)=10
+
+%quit
diff --git a/clang/test/Interpreter/ptu-class-sema-recovery.cpp b/clang/test/Interpreter/ptu-class-sema-recovery.cpp
new file mode 100644
index 0000000000000..5befdd7be33e5
--- /dev/null
+++ b/clang/test/Interpreter/ptu-class-sema-recovery.cpp
@@ -0,0 +1,112 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl | FileCheck %s
+//
+// Sema-focused PTU rollback regression tests, restricted to complex class
+// constructs. Each scenario is adapted from a real clang/test/SemaCXX case
+// and, following that suite's own style, uses a genuine semantic error
+// (deleted/implicitly-deleted special members, access-control violations,
+// undefined partial specializations, ...) rather than a synthetic bad
+// token wherever the construct naturally produces one. The failing chunk
+// always pairs that real error with a legitimate new class on the same
+// line, so a correct rollback must discard both together; the follow-up
+// chunk re-exercises the same class machinery (SpecialMemberCache,
+// access-control checks, partial-specialization matching, ...) to see
+// whether the rollback left it consistent.
+
+extern "C" int printf(const char *, ...);
+
+// 1. Implicitly-deleted copy constructor via a non-copyable member, cf.
+// SemaCXX/cxx11-call-to-deleted-constructor.cpp and
+// SemaCXX/explicitly-defaulted.cpp. Computing "is this special member
+// deleted" for Holder populates Sema::SpecialMemberCache.
+struct NoCopy { NoCopy() = default; NoCopy(const NoCopy&) = delete; };
+struct Holder { NoCopy nc; int tag; Holder(int t) : tag(t) {} };
+Holder h1(1);
+auto rc1 = printf("h1.tag=%d\n", h1.tag);
+// CHECK: h1.tag=1
+
+struct HolderBad { NoCopy nc; int tag; HolderBad(int t) : tag(t) {} }; Holder h_copy_bad = h1;
+Holder h2(2);
+auto rc2 = printf("h2.tag=%d\n", h2.tag);
+// CHECK: h2.tag=2
+
+// 2. Dependent-name member access inside a nested class template, cf.
+// SemaCXX/dependent-types.cpp and SemaCXX/member-class-11.cpp. The bogus
+// member is only diagnosed once Inner::scaled is instantiated for a
+// concrete T, exercising two-phase lookup plus template instantiation
+// bookkeeping (InstantiatingSpecializations, PendingInstantiations).
+template<typename T> struct Outer {
+  T value;
+  struct Inner { T scaled(Outer<T>& o, T factor) { return o.value * factor; } };
+};
+Outer<int> outer1; outer1.value = 5;
+Outer<int>::Inner inner1;
+auto rc3 = printf("scaled=%d\n", inner1.scaled(outer1, 3));
+// CHECK: scaled=15
+
+template<typename T> struct OuterBad { T value; struct Inner { T scaled(OuterBad<T>& o, T factor) { return o.value * o.nonexistent_member * factor; } }; }; OuterBad<int> ob; ob.value = 1; OuterBad<int>::Inner ib; ib.scaled(ob, 2);
+Outer<double> outer2; outer2.value = 2.5;
+Outer<double>::Inner inner2;
+auto rc4 = printf("scaled2=%.1f\n", inner2.scaled(outer2, 4.0));
+// CHECK: scaled2=10.0
+
+// 3. Protected-member access control across an unrelated class, cf.
+// SemaCXX/access-control-check.cpp. ConvOutsider's direct access to
+// ConvBase::secret is a genuine access-control error, alongside a
+// legitimate new derived class on the same line.
+struct ConvBase { protected: int secret = 10; public: int reveal() { return secret; } };
+struct ConvDerived : ConvBase { int shout() { return secret * 2; } };
+ConvDerived cd1;
+auto rc5 = printf("reveal=%d shout=%d\n", cd1.reveal(), cd1.shout());
+// CHECK: reveal=10 shout=20
+
+struct ConvOutsider { int peek(ConvBase& b) { return b.secret; } }; void overload_pick(ConvDerived) {}
+struct ConvDerived2 : ConvBase { int shout() { return secret * 3; } };
+ConvDerived2 cd2;
+auto rc6 = printf("reveal2=%d shout2=%d\n", cd2.reveal(), cd2.shout());
+// CHECK: reveal2=10 shout2=30
+
+// 4. Instantiating an undefined class-template partial specialization, cf.
+// SemaCXX/undefined-partial-specialization.cpp and
+// SemaCXX/identical-type-primary-partial-specialization.cpp.
+template<typename T> struct Describe { static int tag() { return 0; } };
+template<typename T> struct Describe<T*> { static int tag() { return 1; } };
+auto rc7 = printf("tag_val=%d tag_ptr=%d\n", Describe<int>::tag(), Describe<int*>::tag());
+// CHECK: tag_val=0 tag_ptr=1
+
+template<typename T> struct Describe<T&>; template<typename T> struct DescribeBad { static int tag() { return Describe<T&>::tag(); } }; int bad_tag = DescribeBad<int>::tag();
+auto rc8 = printf("tag_val2=%d tag_ptr2=%d\n", Describe<double>::tag(), Describe<double*>::tag());
+// CHECK: tag_val2=0 tag_ptr2=1
+
+// 5. Friend-granted access vs. a genuine private-access violation, cf.
+// SemaCXX/friend.cpp and SemaCXX/friend-out-of-line.cpp.
+struct FriendHolder {
+private:
+  int hidden = 42;
+  friend int peek_hidden(FriendHolder&);
+};
+int peek_hidden(FriendHolder& f) { return f.hidden; }
+FriendHolder fh1;
+auto rc9 = printf("peek_hidden=%d\n", peek_hidden(fh1));
+// CHECK: peek_hidden=42
+
+struct FriendHolder2 { private: int hidden2 = 99; friend int peek_hidden2(FriendHolder2&); }; int bad_peek = fh1.hidden;
+int peek_hidden2(FriendHolder2& f) { return f.hidden2; }
+FriendHolder2 fh2;
+auto rc10 = printf("peek_hidden2=%d\n", peek_hidden2(fh2));
+// CHECK: peek_hidden2=99
+
+// 6. Implicitly-deleted copy-assignment via a non-assignable member, cf.
+// SemaCXX/deleted-function.cpp and SemaCXX/deleted-function-access.cpp.
+struct NoAssign { int val; NoAssign(int v) : val(v) {} NoAssign& operator=(const NoAssign&) = delete; };
+struct Container { NoAssign na; Container(int v) : na(v) {} };
+Container c1(5);
+auto rc11 = printf("c1.na.val=%d\n", c1.na.val);
+// CHECK: c1.na.val=5
+
+struct ContainerBad { NoAssign na; ContainerBad(int v) : na(v) {} }; Container c1b(9); c1 = c1b;
+Container c2(11);
+auto rc12 = printf("c2.na.val=%d\n", c2.na.val);
+// CHECK: c2.na.val=11
+
+%quit
diff --git a/clang/test/Interpreter/ptu-lookup-recovery.cpp b/clang/test/Interpreter/ptu-lookup-recovery.cpp
new file mode 100644
index 0000000000000..9c3317e717185
--- /dev/null
+++ b/clang/test/Interpreter/ptu-lookup-recovery.cpp
@@ -0,0 +1,49 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl | FileCheck %s
+
+extern "C" int printf(const char *, ...);
+
+// 1. Block-scope shadowing rolled back inside a failed function body.
+int shadow_val = 1;
+int use_shadow() { int shadow_val = 2; return shadow_val; }
+auto r1 = printf("shadow_val=%d use_shadow=%d\n", shadow_val, use_shadow());
+// CHECK: shadow_val=1 use_shadow=2
+
+int use_shadow_bad() { int shadow_val = 99; { int shadow_val = 100; (void)shadow_val; } return shadow_val; } intentional_error_type shadow_garbage;
+auto r2 = printf("shadow_val=%d use_shadow=%d\n", shadow_val, use_shadow());
+// CHECK: shadow_val=1 use_shadow=2
+
+// 2. Tag namespace vs ordinary namespace after rollback (IdResolver keeps
+// separate chains per lookup id-namespace for the same spelling).
+struct TagName { int x; };
+
+int TagName = 5; struct TagName also_tag; intentional_error_type tag_garbage;
+struct TagName t3; t3.x = 7;
+int TagName2 = 42;
+auto r3 = printf("t3.x=%d TagName2=%d\n", t3.x, TagName2);
+// CHECK: t3.x=7 TagName2=42
+
+// 3. ADL (argument-dependent lookup) across a rolled-back use site.
+namespace adl_ns { struct Widget {}; int probe(Widget) { return 11; } }
+
+int trigger_adl_bad() { adl_ns::Widget w; return probe(w); } intentional_error_type adl_garbage;
+adl_ns::Widget w2;
+auto r4 = printf("probe=%d\n", probe(w2));
+// CHECK: probe=11
+
+// 4. Enumerator (EnumConstantDecl) shadowing rolled back.
+enum Color { Red, Green, Blue };
+
+int enum_test_bad() { enum Local { Red, Yellow }; return Red; } intentional_error_type enum_garbage;
+int check_global_red() { return Red; }
+auto r5 = printf("Red=%d\n", check_global_red());
+// CHECK: Red=0
+
+// 5. Overload set growth rolled back.
+int overload_fn(int x) { return x + 1; }
+
+int overload_fn(double x) { return (int)(x + 2.5); } intentional_error_type overload_garbage;
+auto r6 = printf("overload_fn(3)=%d\n", overload_fn(3));
+// CHECK: overload_fn(3)=4
+
+%quit
diff --git a/clang/test/Interpreter/ptu-rewind-stress.cpp b/clang/test/Interpreter/ptu-rewind-stress.cpp
new file mode 100644
index 0000000000000..f38b1c5fb9731
--- /dev/null
+++ b/clang/test/Interpreter/ptu-rewind-stress.cpp
@@ -0,0 +1,259 @@
+// REQUIRES: host-supports-jit
+//
+// Usage as a lit test (pipes every line below into clang-repl as a separate
+// incremental input, the same way fail.cpp / code-undo.cpp do):
+//   RUN: cat %s | clang-repl | FileCheck %s
+//
+// Usage interactively: paste blocks (or the whole file) directly at the
+// `clang-repl>` prompt. Each block is a single line on purpose -- clang-repl
+// treats one physical line as one Interpreter::Parse() call, and several of
+// these tests rely on packing a well-formed declaration and an ill-formed
+// one onto the *same* line so both go through Sema together before the
+// chunk is discarded as a whole.
+//
+// -----------------------------------------------------------------------
+// Observed results (assertions-enabled build, this checkout, macOS/arm64)
+// -----------------------------------------------------------------------
+// Running this file as-is through `./bin/clang-repl < ptu-rewind-stress.cpp`
+// does NOT make it to the %quit at the bottom:
+//  * Test 1 (dangling_name) printed "dangling_name = 7" instead of 99, and
+//    the JIT reported
+//      error: In incr_module_89, duplicate definition of symbol '_dangling_name'
+//    i.e. the rolled-back chunk's *codegen* (which runs incrementally per
+//    top-level decl, before the end-of-chunk error check) was never undone
+//    either, so the second, successful declaration silently reused the
+//    first (supposedly-discarded) global.
+//  * Test 3 (Boom<int> template instantiation) reliably aborts the process:
+//      Assertion failed: (CP.UndefinedButUsedSize == S.UndefinedButUsed.size()),
+//      function restore, file SemaStateStash.cpp, line 457.
+//    i.e. real, confirmed, unconditional test-3-and-later block; a debug/
+//    assertions build never reaches tests 4-7 in one session because of
+//    this abort.
+//  * Running tests 4-7 in isolation (skipping 1-3), Test 4 (sizeof on a
+//    pre-existing type) produced
+//      JIT session error: Symbols not found: [ _sz_known2 ]
+//      error: Failed to materialize symbols: ...
+//    followed by the process spinning at 100% CPU instead of returning to
+//    the prompt for the remaining input (tests 5-7 never ran in that
+//    session either).
+//  * Running Test 1 completely on its own (no preceding chunks) did NOT
+//    reproduce the wrong-value/duplicate-symbol failure -- it printed the
+//    correct "dangling_name = 99". This is expected for a poisoned/reused-
+//    memory bug: whether a stale pointer's target has been overwritten by
+//    something that "looks wrong" depends on what the allocator handed out
+//    to *other* code in between, so the manifestation is sensitive to the
+//    exact preceding session history, not just to the isolated snippet.
+//    That is precisely why this file chains many small scenarios in one
+//    session rather than shipping them as independent one-liners.
+//
+// -----------------------------------------------------------------------
+// Background
+// -----------------------------------------------------------------------
+// Interpreter::Parse (clang/lib/Interpreter/Interpreter.cpp) wraps every
+// incremental input in a PTUSlabRollback guard:
+//   1. Takes CheckPoint = Ctx.getAllocator().checkPoint()
+//   2. Stashes ASTContext/Sema side-table sizes (ASTContextStateStash,
+//      SemaStateStash)
+//   3. Runs IncrementalParser::Parse()
+//   4. On success: commits (keeps everything)
+//   5. On failure: SemaState.restore(), ASTCtxState.restore(), then
+//      Ctx.getAllocator().restoreToCheckPoint(CheckPoint)
+//
+// restoreToCheckPoint (llvm/include/llvm/Support/Allocator.h) does not just
+// move a pointer back -- it actively memset()s the reclaimed range to 0xCD
+// ("poisonMemory") outside of ASan builds. So *any* pointer left behind in a
+// side-table that the two StateStash::restore() functions fail to clean up
+// is not merely suspect, it is guaranteed to point at either poisoned bytes
+// or, once new allocations reuse that space, at a completely unrelated
+// object (type confusion).
+//
+// Reading ASTContextStateStash.cpp and SemaStateStash.cpp shows the
+// restore() paths fall into three buckets:
+//   (a) real cleanup: erase from the container based on
+//       isAfterCheckpoint(ptr, SlabCP)  [handles most Type folding sets]
+//   (b) resize()/pop-back style cleanup on trailing-append vectors
+//       [FunctionScopes, LateParsedInstantiations, SavedVTableUses, ...]
+//   (c) `assert(oldSize == newSize)` with NO actual erase -- compiled away
+//       entirely under NDEBUG, so the container is left holding dangling
+//       pointers with no diagnostic at all in a release build
+//       [StringLiteralCache, KeyFunctions-adjacent maps when the early
+//       return below fires, MergedDecls, TemplateInstCallbacks,
+//       PendingInstantiations, LateParsedTemplateMap, VTableUses/
+//       VTablesUsed, ...]
+// Additionally, ASTContextStateStash::restore() opens with:
+//       if (CP.TypesSize == Ctx.Types.size()) return;
+// which skips *all* of the above (including the folding sets that do have
+// real cleanup logic) whenever the number of interned Type nodes happens to
+// be unchanged -- even though many other caches (record layouts, key
+// functions, ...) can grow without interning a new Type.
+//
+// Every test below is built to land in one of these gaps. None of the
+// "poison" code paths depend on undefined behavior sanitizers to observe --
+// under a debug (assertions-enabled) build several of them should abort on
+// the assert() itself; under a release build the same inputs should
+// eventually crash, print garbage, or misbehave once the poisoned/reused
+// memory is dereferenced.
+// -----------------------------------------------------------------------
+
+extern "C" int printf(const char *, ...);
+
+// =======================================================================
+// Test 1: Sema::IdResolver keeps a dangling entry after a failed parse.
+//
+// SemaStateStash::restore() (clang/lib/Interpreter/SemaStateStash.cpp)
+// walks S.getCurScope()->decls() and calls Scope::RemoveDecl() for every
+// decl allocated after the checkpoint -- but the matching
+// `S.IdResolver.RemoveDecl(D)` call right above it is commented out. Name
+// lookup goes through IdResolver, not just Scope, so `dangling_name`'s
+// VarDecl stays "found" by lookup even though its storage is about to be
+// poisoned by the allocator rewind.
+//
+// `int dangling_name = 7;` fully binds (Scope + IdResolver + DeclContext
+// lookup map) before `intentional_error_type` is even parsed, because
+// IncrementalParser::ParseOrWrapTopLevelDecl only checks
+// Diags.hasErrorOccurred() *after* parsing every top-level decl in the
+// chunk. So both statements are discarded together, but only Scope forgets
+// about `dangling_name`.
+// =======================================================================
+int dangling_name = 7; intentional_error_type garbage_after_dangling_name;
+
+// Redeclare the same identifier in a fresh, successful chunk. If the stale
+// IdResolver entry survived, Sema's redeclaration-merging logic
+// (Sema::MergeVarDecl* et al.) may try to compare this new VarDecl against
+// the dangling one -- reading 0xCD-poisoned memory, or memory since reused
+// by an unrelated allocation.
+int dangling_name = 99;
+auto t1 = printf("dangling_name = %d\n", dangling_name);
+// CHECK: dangling_name = 99
+
+// =======================================================================
+// Test 2: Amplify test 1 by repeating the fail/redeclare cycle several
+// times on the same identifier. Because every failed chunk takes its
+// checkpoint at (almost) the same allocator offset, each failed attempt's
+// storage for `stress_var` gets reallocated over the *same* address range
+// poisoned by the previous attempt. Any stale IdResolver node left behind
+// by an earlier iteration therefore ends up aliasing whatever the *next*
+// iteration (or the final, successful declaration) allocates there --
+// classic type-confusion setup, and a good candidate to run under ASan.
+// =======================================================================
+int stress_var = 0; intentional_error_type e0;
+int stress_var = 1; intentional_error_type e1;
+int stress_var = 2; intentional_error_type e2;
+int stress_var = 3; intentional_error_type e3;
+int stress_var = 4; intentional_error_type e4;
+int stress_var = 42;
+auto t2 = printf("stress_var = %d\n", stress_var);
+// CHECK: stress_var = 42
+
+// =======================================================================
+// Test 3: Sema::PendingInstantiations does not survive a failed parse.
+//
+// ParseOrWrapTopLevelDecl() returns *before* calling
+// LocalInstantiations.perform()/GlobalInstantiations.perform() whenever
+// Diags.hasErrorOccurred() -- see the early `return` right after
+// CleanUpPTU() in IncrementalParser.cpp. So any instantiation work queued
+// while parsing `Boom<int> boom_instance; boom_instance.trigger();` is never
+// drained on this path. SemaStateStash::restore() only compares
+// S.PendingInstantiations.size() with an assert (it's a std::deque, never
+// resized/erased), so a stale PendingImplicitInstantiation entry pointing
+// at the (about-to-be-poisoned) `Boom<int>::trigger` specialization can
+// leak into whatever the *next* successful chunk's own eager-instantiation
+// pass processes.
+// =======================================================================
+template <typename T> struct Boom { void trigger() { T v; (void)v; } };
+Boom<int> boom_instance; boom_instance.trigger(); intentional_error_type boom_garbage;
+
+// This chunk's own GlobalEagerInstantiationScope/LocalEagerInstantiationScope
+// pass (constructed fresh in every ParseOrWrapTopLevelDecl call) is where a
+// leftover PendingInstantiations entry from the failed chunk above would
+// get processed against poisoned memory.
+int after_boom_marker = 1;
+auto t3 = printf("after_boom_marker = %d\n", after_boom_marker);
+// CHECK: after_boom_marker = 1
+
+// =======================================================================
+// Test 4: ASTContextStateStash's Types.size() early-return guard skips
+// cleanup of caches that don't depend on interning a new Type.
+//
+//   void ASTContextStateStash::restore(...) {
+//     if (CP.TypesSize == Ctx.Types.size())
+//       return;
+//     ... (all the real per-folding-set cleanup lives below this line) ...
+//
+// `AlreadyKnown` already exists (its RecordType was interned when it was
+// first declared, in the prior committed chunk). Computing sizeof() on it
+// only populates Ctx.ASTRecordLayouts (and, since it's a POD aggregate here,
+// nothing else) -- it does not intern a new Type, so Ctx.Types.size() is
+// unchanged across this failing chunk and the whole restore() body,
+// including ASTRecordLayouts's own (otherwise correct) erase-by-checkpoint
+// logic, is skipped.
+// =======================================================================
+struct AlreadyKnown { int a; int b; };
+unsigned long sz_known = sizeof(AlreadyKnown); intentional_error_type sizeof_garbage;
+
+// Recomputing sizeof() on the same, still-live type must not dereference a
+// stale ASTRecordLayout* left behind by the chunk above.
+unsigned long sz_known2 = sizeof(AlreadyKnown);
+auto t4 = printf("sizeof(AlreadyKnown) = %lu\n", sz_known2);
+// CHECK: sizeof(AlreadyKnown) = 8
+
+// =======================================================================
+// Test 5: Virtual dispatch bookkeeping (Sema::VTableUses/VTablesUsed,
+// ASTContext::KeyFunctions) is assert-only / guard-gated, same as above,
+// but exercised through polymorphic classes and `new` instead of sizeof.
+// RecordLayoutBuilder.cpp populates Ctx.KeyFunctions[RD] while laying out
+// `Derived` for the `new` expression below; Sema::MarkVTableUsed populates
+// VTableUses/VTablesUsed. Both are torn down on a *committed* chunk but
+// only assert-compared on a rolled-back one.
+// =======================================================================
+struct Base { virtual int val() { return 1; } virtual ~Base() {} };
+struct Derived : Base { int val() override { return 2; } };
+Base* bp = new Derived(); int vv = bp->val(); intentional_error_type vtable_garbage;
+
+// A fresh Base*/Derived pair exercising the same key-function/vtable-use
+// bookkeeping. If the discarded attempt above left stale KeyFunctions /
+// VTablesUsed state pointing at poisoned memory, codegen for *this* vtable
+// can be skipped, corrupted, or crash outright.
+Base* bp2 = new Derived();
+auto t5 = printf("val = %d\n", bp2->val());
+// CHECK: val = 2
+
+// =======================================================================
+// Test 6: Ctx.StringLiteralCache has no erase logic at all -- not gated by
+// the Types.size() guard (defining a brand-new function interns a new
+// FunctionProtoType, so the early return above does *not* fire here), just
+// a bare `assert(CP.StringLiteralCacheSize == Ctx.StringLiteralCache.size())`
+// with nothing to actually undo the insertion made by
+// ASTContext::getPredefinedStringLiteralFromCache when Sema processes
+// __PRETTY_FUNCTION__. In an assertions-enabled build this is the test most
+// likely to abort immediately and deterministically, independent of memory
+// poisoning, purely because the size check itself fails.
+// =======================================================================
+void uses_predefined_expr() { const char* pf = __PRETTY_FUNCTION__; (void)pf; } intentional_error_type predefined_garbage;
+
+// A second, differently-named function that also uses __PRETTY_FUNCTION__,
+// forcing another StringLiteralCache lookup/insert keyed differently from
+// the discarded one above.
+void uses_predefined_expr_again() { const char* pf = __PRETTY_FUNCTION__; (void)pf; }
+int pf_ran = 1;
+auto t6 = printf("pf_ran = %d\n", pf_ran);
+// CHECK: pf_ran = 1
+
+// =======================================================================
+// Test 7: Cross-declaration merging state (Ctx.MergedDecls,
+// Ctx.InstantiatedFromUsingShadowDecl) survives a failed using-declaration.
+// A `using` declaration creates a UsingShadowDecl tied back to the
+// original NS::helper via a side map that SemaStateStash/
+// ASTContextStateStash only assert-compare. If the failed attempt's
+// UsingShadowDecl (and its bookkeeping entry) is left dangling, a
+// subsequent successful `using NS::helper;` shares the same DeclarationName
+// and could resolve through, or conflict with, the stale shadow chain.
+// =======================================================================
+namespace NS { int helper() { return 10; } }
+using NS::helper; intentional_error_type merge_garbage;
+
+using NS::helper;
+auto t7 = printf("helper() = %d\n", helper());
+// CHECK: helper() = 10
+
+%quit

>From f0f8e889e8ae7000f2ee9fe8910979b55d280282 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 13 Sep 2026 14:34:46 +0530
Subject: [PATCH 03/13] Add Initial impl ErrorRecovery.h

---
 clang/include/clang/AST/ASTContext.h          |   19 +-
 clang/include/clang/AST/ASTMutationListener.h |    6 +
 clang/include/clang/AST/Decl.h                |    6 +-
 clang/include/clang/AST/DeclBase.h            |   19 +-
 clang/include/clang/AST/DeclCXX.h             |    3 +
 .../include/clang/AST/DeclContextInternals.h  |    4 +-
 clang/include/clang/AST/DeclTemplate.h        |    6 +
 clang/include/clang/AST/DeclarationName.h     |    2 +
 clang/include/clang/AST/RecordLayout.h        |    4 +-
 clang/include/clang/AST/Redeclarable.h        |    4 +
 .../include/clang/Interpreter/ErrorRecovery.h | 1404 ++++++++++++++++-
 clang/include/clang/Sema/Sema.h               |    4 +-
 clang/lib/AST/ASTContext.cpp                  |   11 +-
 clang/lib/AST/DeclBase.cpp                    |    3 -
 clang/lib/Frontend/MultiplexConsumer.cpp      |    5 +
 .../lib/Interpreter/ASTContextStateStash.cpp  |  461 +++++-
 clang/lib/Interpreter/IncrementalAction.cpp   |   12 +-
 clang/lib/Interpreter/IncrementalAction.h     |    4 +-
 clang/lib/Interpreter/IncrementalParser.cpp   |   11 +-
 clang/lib/Interpreter/Interpreter.cpp         |   10 +-
 clang/lib/Interpreter/SemaStateStash.cpp      |   78 +-
 clang/test/Interpreter/ptu-rewind-stress.cpp  |  171 +-
 llvm/include/llvm/ADT/FoldingSet.h            |    5 +
 llvm/include/llvm/Support/Allocator.h         |   24 +-
 24 files changed, 1916 insertions(+), 360 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 01f83b4d85113..aaefe14fa828d 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -85,7 +85,7 @@ template <> struct DenseMapInfo<ScalableVecTyKey> {
 namespace clang {
 
 class APValue;
-class ASTContextStateStash;
+class ASTContextStateRecovery;
 class ASTMutationListener;
 class ASTRecordLayout;
 class AtomicExpr;
@@ -235,11 +235,6 @@ struct QualTypeBoolInfo {
   }
 };
 
-struct TypeForDeclMutation {
-  TypeDecl *Decl;
-  const Type *OldValue;
-};
-
 /// Holds long-lived AST nodes (such as types and decls) that can be
 /// referred to throughout the semantic analysis of a file.
 class ASTContext : public RefCountedBase<ASTContext> {
@@ -247,10 +242,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
 
   mutable SmallVector<Type *, 0> Types;
 
-  bool IncrementalErrorRecoveryMode = true;
-  mutable SmallVector<TypeForDeclMutation> PendingTypeForDeclMutations;
-  mutable llvm::DenseSet<const DeclContext *> PendingDCMutations;
-
   mutable llvm::FoldingSet<ExtQuals> ExtQualNodes;
   mutable llvm::UniquingSet<ComplexType> ComplexTypes;
   mutable llvm::UniquingSet<PointerType> PointerTypes{GeneralTypesLog2InitSize};
@@ -616,18 +607,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
   using TemplateOrSpecializationInfo =
       llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *>;
 
-  bool isIncrementalErrorRecoveryMode() const {
-    return IncrementalErrorRecoveryMode;
-  }
-
 private:
   friend class ASTDeclReader;
   friend class ASTReader;
   friend class ASTWriter;
   template <class> friend class serialization::AbstractTypeReader;
   friend class CXXRecordDecl;
-  // friend class IncrementalParser;
-  friend class ASTContextStateStash;
+  friend class IncrementalParser;
+  friend class ASTContextStateRecovery;
 
   /// A mapping to contain the template or declaration that
   /// a variable declaration describes or was instantiated from,
diff --git a/clang/include/clang/AST/ASTMutationListener.h b/clang/include/clang/AST/ASTMutationListener.h
index 7540b7f90ee86..14cb3b1d14612 100644
--- a/clang/include/clang/AST/ASTMutationListener.h
+++ b/clang/include/clang/AST/ASTMutationListener.h
@@ -195,6 +195,12 @@ class ASTMutationListener {
   virtual void AddedAnonymousNamespace(const TranslationUnitDecl *TU,
                                        NamespaceDecl *AnonNamespace) {}
 
+  /// A TagDecl's cached TypeForDecl was materialized for the first time.
+  ///
+  /// \param TD The declaration whose type cache was populated.
+  /// \param T The Type now cached. Never null.
+  virtual void AddedTagDeclType(const TagDecl *TD, const Type *T) {}
+
   // NOTE: If new methods are added they should also be added to
   // MultiplexASTMutationListener.
 };
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index d5b7f6240d134..05a102d584a57 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -57,6 +57,7 @@
 namespace clang {
 
 class ASTContext;
+class ASTStateReader;
 struct ASTTemplateArgumentListInfo;
 class CompoundStmt;
 class DependentFunctionTemplateSpecializationInfo;
@@ -3644,13 +3645,11 @@ class IndirectFieldDecl : public ValueDecl,
   static bool classofKind(Kind K) { return K == IndirectField; }
 };
 
-class ASTContextStateStash;
-
 /// Represents a declaration of a type.
 class TypeDecl : public NamedDecl {
   friend class ASTContext;
-  friend class ASTContextStateStash;
   friend class ASTReader;
+  friend class ASTStateReader;
 
   /// This indicates the Type object that represents
   /// this TypeDecl.  It is a cache maintained by
@@ -3917,6 +3916,7 @@ class TagDecl : public TypeDecl,
 
 public:
   friend class ASTDeclReader;
+  friend class ASTStateReader;
   friend class ASTDeclWriter;
 
   using redecl_range = redeclarable_base::redecl_range;
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 9ec6f4c84a857..9dd1f60011ce0 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -43,10 +43,13 @@
 namespace clang {
 
 class ASTContext;
+class ASTContextStateRecovery;
+class ASTDeclDetacher;
 class ASTMutationListener;
 class Attr;
 class BlockDecl;
 class DeclContext;
+class DeclContextRepairer;
 class ExternalSourceSymbolAttr;
 class FunctionDecl;
 class FunctionType;
@@ -76,8 +79,6 @@ enum AvailabilityResult {
   AR_Unavailable
 };
 
-class ASTContextStateStash;
-
 /// Decl - This represents one declaration (or definition), e.g. a variable,
 /// typedef, function, struct, etc.
 ///
@@ -260,7 +261,9 @@ class alignas(8) Decl {
 
 private:
   friend class DeclContext;
-  friend class ASTContextStateStash;
+  // friend class ASTContextStateRecovery;
+  friend class ASTDeclDetacher;
+  friend class DeclContextRepairer;
 
   struct MultipleDC {
     DeclContext *SemanticDC;
@@ -1345,11 +1348,10 @@ namespace llvm {
 }
 
 namespace clang {
-class ASTContextStateStash;
 /// A list storing NamedDecls in the lookup tables.
 class DeclListNode {
   friend class ASTContext; // allocate, deallocate nodes.
-  friend class ASTContextStateStash;
+  friend class ASTContextStateRecovery;
   friend class StoredDeclsList;
 public:
   using Decls = llvm::PointerUnion<NamedDecl*, DeclListNode*>;
@@ -1451,8 +1453,6 @@ enum class OMPDeclareReductionInitKind;
 enum class ObjCImplementationControl;
 enum class LinkageSpecLanguageIDs;
 
-class ASTContextStateStash;
-
 /// DeclContext - This is used only as base class of specific decl types that
 /// can act as declaration contexts. These decls are (only the top classes
 /// that directly derive from DeclContext are mentioned, not their subclasses):
@@ -1471,7 +1471,10 @@ class ASTContextStateStash;
 ///   BlockDecl
 ///   CapturedDecl
 class DeclContext {
-  friend class ASTContextStateStash;
+  /// For restoring decl to its previous state (clang-repl error-recovery).
+  friend class ASTDeclDetacher;
+
+  friend class DeclContextRepairer;
   /// For makeDeclVisibleInContextImpl
   friend class ASTDeclReader;
   /// For checking the new bits in the Serialization part.
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 1ebc8a7191bde..539cf28a76031 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -54,6 +54,8 @@
 namespace clang {
 
 class ASTContext;
+class ASTDeclDetacher;
+class ASTStateReader;
 class ClassTemplateDecl;
 class ConstructorUsingShadowDecl;
 class CXXBasePath;
@@ -263,6 +265,7 @@ class CXXRecordDecl : public RecordDecl {
   friend class ASTNodeImporter;
   friend class ASTReader;
   friend class ASTRecordWriter;
+  friend class ASTStateReader;
   friend class ASTWriter;
   friend class DeclContext;
   friend class LambdaExpr;
diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h
index f753e1e8f9eb4..e7d9c2d742dc8 100644
--- a/clang/include/clang/AST/DeclContextInternals.h
+++ b/clang/include/clang/AST/DeclContextInternals.h
@@ -301,13 +301,13 @@ class StoredDeclsList {
   }
 };
 
-class ASTContextStateStash;
+class ASTContextStateRecovery;
 
 class StoredDeclsMap
     : public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> {
   friend class ASTContext; // walks the chain deleting these
   friend class DeclContext;
-  friend class ASTContextStateStash;
+  friend class ASTContextStateRecovery;
 
   llvm::PointerIntPair<StoredDeclsMap*, 1> Previous;
 public:
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index d32b044ef40e2..a4beffb99465b 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -46,6 +46,8 @@
 
 namespace clang {
 
+class ASTDeclDetacher;
+class ASTStateReader;
 enum BuiltinTemplateKind : int;
 class ClassTemplateDecl;
 class ClassTemplatePartialSpecializationDecl;
@@ -824,7 +826,10 @@ class RedeclarableTemplateDecl : public TemplateDecl,
 public:
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
+  friend class ASTDeclDetacher;
   friend class ASTReader;
+  friend class ASTStateReader;
+
   template <class decl_type> friend class RedeclarableTemplate;
 
   /// Retrieves the canonical declaration of this template.
@@ -2312,6 +2317,7 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
 
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
+  friend class ASTStateReader;
   friend class TemplateDeclInstantiator;
 
   /// Load any lazily-loaded specializations from the external source.
diff --git a/clang/include/clang/AST/DeclarationName.h b/clang/include/clang/AST/DeclarationName.h
index 5121b561288c0..dd490c688de18 100644
--- a/clang/include/clang/AST/DeclarationName.h
+++ b/clang/include/clang/AST/DeclarationName.h
@@ -32,6 +32,7 @@
 namespace clang {
 
 class ASTContext;
+class ASTContextStateRecovery;
 template <typename> class CanQual;
 class DeclarationName;
 class DeclarationNameTable;
@@ -591,6 +592,7 @@ inline bool operator>=(DeclarationName LHS, DeclarationName RHS) {
 /// uniqued versions of each of the C++ special names, which can be
 /// retrieved using its member functions (e.g., getCXXConstructorName).
 class DeclarationNameTable {
+  friend class ASTContextStateRecovery;
   /// Used to allocate elements in the FoldingSets below.
   const ASTContext &Ctx;
 
diff --git a/clang/include/clang/AST/RecordLayout.h b/clang/include/clang/AST/RecordLayout.h
index 73ecb90fbcd88..a7964b69feb03 100644
--- a/clang/include/clang/AST/RecordLayout.h
+++ b/clang/include/clang/AST/RecordLayout.h
@@ -26,7 +26,7 @@
 namespace clang {
 
 class ASTContext;
-class ASTContextStateStash;
+class ASTContextStateRecovery;
 class CXXRecordDecl;
 
 /// ASTRecordLayout -
@@ -61,7 +61,7 @@ class ASTRecordLayout {
 
 private:
   friend class ASTContext;
-  friend class ASTContextStateStash;
+  friend class ASTContextStateRecovery;
 
   /// Size - Size of record in characters.
   CharUnits Size;
diff --git a/clang/include/clang/AST/Redeclarable.h b/clang/include/clang/AST/Redeclarable.h
index fe2648aa58301..150c310530c03 100644
--- a/clang/include/clang/AST/Redeclarable.h
+++ b/clang/include/clang/AST/Redeclarable.h
@@ -25,7 +25,9 @@
 namespace clang {
 
 class ASTContext;
+class ASTDeclDetacher;
 class Decl;
+class DeclContextRepairer;
 
 // Some notes on redeclarables:
 //
@@ -189,6 +191,8 @@ class Redeclarable {
   friend class ASTDeclUnmerger;
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
+  friend class ASTDeclDetacher;
+  friend class DeclContextRepairer;
   friend class IncrementalParser;
 
   Redeclarable(const ASTContext &Ctx)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 245d9416a8933..0c47e9bc2296b 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -13,11 +13,16 @@
 #ifndef LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
 #define LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
 
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTMutationListener.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
 #include "clang/Sema/Sema.h"
 
+#include "llvm/ADT/DenseMap.h"
+
 namespace clang {
 class ASTContext;
-class Sema;
 
 struct SemaStashCheckPoint {
   /// Sema::*
@@ -98,18 +103,19 @@ struct SemaStashCheckPoint {
   size_t CodeSynthesisContextLookupModulesSize = 0;
   size_t LookupModulesCacheSize = 0;
   size_t VisibleNamespaceCacheSize = 0;
-  size_t TemplateInstCallbacksSize = 0;
+  // size_t TemplateInstCallbacksSize = 0;
   size_t PendingInstantiationsSize = 0;
   size_t LateParsedInstantiationsSize = 0;
   size_t SavedVTableUsesSize = 0;
   size_t SavedPendingInstantiationsSize = 0;
   size_t PendingLocalImplicitInstantiationsSize = 0;
+  size_t CurrentCachedTemplateArgsSize = 0;
   size_t UnsubstitutedConstraintSatisfactionCacheSize = 0;
   size_t SubsumptionCacheSize = 0;
   size_t NormalizationCacheSize = 0;
   size_t SatisfactionCacheSize = 0;
   size_t SatisfactionStackSize = 0;
-//   size_t NullabilityMapSize = 0;
+  //   size_t NullabilityMapSize = 0;
   size_t DeclsWithEffectsToVerifySize = 0;
   //   size_t AllEffectsToVerifySize = 0;
 };
@@ -118,12 +124,12 @@ struct SemaStashCheckPoint {
 ///
 /// Usage:
 ///   SemaStashCheckPoint CP;
-///   SemaStateStash Stash(S);
+///   SemaStateRecovery Stash(S);
 ///   Stash.stash(CP);
 ///   // ... parse ...
 ///   if (failed)
 ///     Stash.restore(CP, ASTSlabCP);
-class SemaStateStash {
+class SemaStateRecovery {
   Sema &S;
 
   /// Holds full copies of PragmaStack, PragmaClangSection, FileNullabilityMap,
@@ -132,11 +138,8 @@ class SemaStateStash {
   //   std::unique_ptr<PragmaSnapshot> Pragmas;
 
 public:
-  explicit SemaStateStash(Sema &S) : S(S) {}
-  //   ~SemaStateStash();
-
-  //   SemaStateStash(const SemaStateStash &) = delete;
-  //   SemaStateStash &operator=(const SemaStateStash &) = delete;
+  explicit SemaStateRecovery(Sema &S) : S(S) {}
+  //   ~SemaStateRecovery();
 
   void stash(SemaStashCheckPoint &CP);
   void restore(SemaStashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
@@ -243,7 +246,7 @@ struct StashCheckPoint {
   size_t ArrayOperatorDeletesForVirtualDtorSize = 0;
   size_t GlobalArrayOperatorDeletesForVirtualDtorSize = 0;
 
-  size_t RequireVectorDeletingDtorSize = 0;
+  size_t MaybeRequireVectorDeletingDtorSize = 0;
 
   size_t MergedDeclsSize = 0;
   size_t DeclAttrsSize = 0;
@@ -255,7 +258,7 @@ struct StashCheckPoint {
 
   size_t ScalableVecTyMapSize = 0;
   size_t LambdaCastPathsSize = 0;
-  size_t DeclRawCommentsSize = 0;
+  size_t RawCommentsSize = 0;
   size_t RedeclChainCommentsSize = 0;
   size_t CommentlessRedeclChainsSize = 0;
   size_t ParsedCommentsSize = 0;
@@ -290,26 +293,1393 @@ struct StashCheckPoint {
   mutable ObjCInterfaceDecl *ObjCProtocolClassDeclCP = nullptr;
 
   // llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
-//    = llvm::PointerIntPair<StoredDeclsMap *, 1>(nullptr, 0);
+  //    = llvm::PointerIntPair<StoredDeclsMap *, 1>(nullptr, 0);
   mutable QualType AutoDeductTy;     // Deduction against 'auto'.
   mutable QualType AutoRRefDeductTy; // Deduction against 'auto &&'.
 
   // mutable DeclarationNameTable DeclarationNames; need to revert.
+  struct DeclarationNamesCP {
+    size_t CXXConstructorNamesSize = 0;
+    size_t CXXDestructorNamesSize = 0;
+    size_t CXXConversionFunctionNamesSize = 0;
+    detail::CXXOperatorIdName CXXOperatorNamesCP[NUM_OVERLOADED_OPERATORS];
+
+    size_t CXXLiteralOperatorNamesSize = 0;
+
+    size_t CXXDeductionGuideNamesSize = 0;
+  };
+
+  DeclarationNamesCP DeclarationNames;
 };
 
-class ASTContextStateStash {
+class ASTContextStateRecovery {
 private:
   ASTContext &Ctx;
 
 public:
-  explicit ASTContextStateStash(ASTContext &Ctx) : Ctx(Ctx) {}
+  explicit ASTContextStateRecovery(ASTContext &Ctx) : Ctx(Ctx) {}
 
-  ASTContextStateStash(const ASTContextStateStash &) = delete;
-  ASTContextStateStash &operator=(const ASTContextStateStash &) = delete;
+  ASTContextStateRecovery(const ASTContextStateRecovery &) = delete;
+  ASTContextStateRecovery &operator=(const ASTContextStateRecovery &) = delete;
 
   void stash(StashCheckPoint &CP);
   void restore(StashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
   void commit();
 };
+
+/// Index of a per PTU State.
+using PTUID = unsigned;
+
+// A snapshot of the handful of DefinitionData bits/bitfields that can
+// change after a class (or class template specialization -- the two are
+// handled identically here since a specialization is non-dependent by the
+// time it's instantiated) is otherwise "done" being defined.
+//
+// These fields only move when the compiler generates implicit special
+// members.
+//
+// This footprint exists purely to undo the effects of implicit special
+// member generation.
+struct DefinitionDataFootprint {
+  unsigned Aggregate : 1;
+  unsigned PlainOldData : 1;
+  unsigned Empty : 1;
+  unsigned Polymorphic : 1;
+  unsigned IsStandardLayout : 1;
+  unsigned IsCXX11StandardLayout : 1;
+  unsigned HasTrivialSpecialMembers : 6;
+  unsigned HasTrivialSpecialMembersForCall : 6;
+  unsigned DeclaredNonTrivialSpecialMembers : 6;
+  unsigned DeclaredNonTrivialSpecialMembersForCall : 6;
+  unsigned HasIrrelevantDestructor : 1;
+  unsigned HasConstexprNonCopyMoveConstructor : 1;
+  unsigned HasDefaultedDefaultConstructor : 1;
+  unsigned HasConstexprDefaultConstructor : 1;
+  unsigned HasDeclaredCopyConstructorWithConstParam : 1;
+  unsigned HasDeclaredCopyAssignmentWithConstParam : 1;
+  unsigned IsAnyDestructorNoReturn : 1;
+  unsigned DeclaredSpecialMembers : 6;
+
+  bool operator==(const DefinitionDataFootprint &O) const {
+    return Aggregate == O.Aggregate && PlainOldData == O.PlainOldData &&
+           Empty == O.Empty && Polymorphic == O.Polymorphic &&
+           IsStandardLayout == O.IsStandardLayout &&
+           IsCXX11StandardLayout == O.IsCXX11StandardLayout &&
+           HasTrivialSpecialMembers == O.HasTrivialSpecialMembers &&
+           HasTrivialSpecialMembersForCall ==
+               O.HasTrivialSpecialMembersForCall &&
+           DeclaredNonTrivialSpecialMembers ==
+               O.DeclaredNonTrivialSpecialMembers &&
+           DeclaredNonTrivialSpecialMembersForCall ==
+               O.DeclaredNonTrivialSpecialMembersForCall &&
+           HasIrrelevantDestructor == O.HasIrrelevantDestructor &&
+           HasConstexprNonCopyMoveConstructor ==
+               O.HasConstexprNonCopyMoveConstructor &&
+           HasDefaultedDefaultConstructor == O.HasDefaultedDefaultConstructor &&
+           HasConstexprDefaultConstructor == O.HasConstexprDefaultConstructor &&
+           HasDeclaredCopyConstructorWithConstParam ==
+               O.HasDeclaredCopyConstructorWithConstParam &&
+           HasDeclaredCopyAssignmentWithConstParam ==
+               O.HasDeclaredCopyAssignmentWithConstParam &&
+           IsAnyDestructorNoReturn == O.IsAnyDestructorNoReturn &&
+           DeclaredSpecialMembers == O.DeclaredSpecialMembers;
+  }
+  bool operator!=(const DefinitionDataFootprint &O) const {
+    return !(*this == O);
+  }
+
+  // void update(const CXXRecordDecl::DefinitionData &Live) {
+  //   Aggregate = Live.Aggregate;
+  //   PlainOldData = Live.PlainOldData;
+  //   Empty = Live.Empty;
+  //   Polymorphic = Live.Polymorphic;
+  //   IsStandardLayout = Live.IsStandardLayout;
+  //   IsCXX11StandardLayout = Live.IsCXX11StandardLayout;
+  //   HasTrivialSpecialMembers = Live.HasTrivialSpecialMembers;
+  //   HasTrivialSpecialMembersForCall = Live.HasTrivialSpecialMembersForCall;
+  //   DeclaredNonTrivialSpecialMembers = Live.DeclaredNonTrivialSpecialMembers;
+  //   DeclaredNonTrivialSpecialMembersForCall =
+  //       Live.DeclaredNonTrivialSpecialMembersForCall;
+  //   HasIrrelevantDestructor = Live.HasIrrelevantDestructor;
+  //   HasConstexprNonCopyMoveConstructor =
+  //       Live.HasConstexprNonCopyMoveConstructor;
+  //   HasDefaultedDefaultConstructor = Live.HasDefaultedDefaultConstructor;
+  //   HasConstexprDefaultConstructor = Live.HasConstexprDefaultConstructor;
+  //   HasDeclaredCopyConstructorWithConstParam =
+  //       Live.HasDeclaredCopyConstructorWithConstParam;
+  //   HasDeclaredCopyAssignmentWithConstParam =
+  //       Live.HasDeclaredCopyAssignmentWithConstParam;
+  //   IsAnyDestructorNoReturn = Live.IsAnyDestructorNoReturn;
+  //   DeclaredSpecialMembers = Live.DeclaredSpecialMembers;
+  // }
+
+  // void restore(CXXRecordDecl::DefinitionData &Live) const {
+  //   Live.Aggregate = Aggregate;
+  //   Live.PlainOldData = PlainOldData;
+  //   Live.Empty = Empty;
+  //   Live.Polymorphic = Polymorphic;
+  //   Live.IsStandardLayout = IsStandardLayout;
+  //   Live.IsCXX11StandardLayout = IsCXX11StandardLayout;
+  //   Live.HasTrivialSpecialMembers = HasTrivialSpecialMembers;
+  //   Live.HasTrivialSpecialMembersForCall = HasTrivialSpecialMembersForCall;
+  //   Live.DeclaredNonTrivialSpecialMembers = DeclaredNonTrivialSpecialMembers;
+  //   Live.DeclaredNonTrivialSpecialMembersForCall =
+  //       DeclaredNonTrivialSpecialMembersForCall;
+  //   Live.HasIrrelevantDestructor = HasIrrelevantDestructor;
+  //   Live.HasConstexprNonCopyMoveConstructor =
+  //       HasConstexprNonCopyMoveConstructor;
+  //   Live.HasDefaultedDefaultConstructor = HasDefaultedDefaultConstructor;
+  //   Live.HasConstexprDefaultConstructor = HasConstexprDefaultConstructor;
+  //   Live.HasDeclaredCopyConstructorWithConstParam =
+  //       HasDeclaredCopyConstructorWithConstParam;
+  //   Live.HasDeclaredCopyAssignmentWithConstParam =
+  //       HasDeclaredCopyAssignmentWithConstParam;
+  //   Live.IsAnyDestructorNoReturn = IsAnyDestructorNoReturn;
+  //   Live.DeclaredSpecialMembers = DeclaredSpecialMembers;
+  // }
+};
+
+// Stores the state of a class template specialization. Tracks its
+// specialization kind, point of instantiation, source location, and lexical
+// declaration context so the state can be compared and restored.
+struct SpecializationFootprint {
+  unsigned SpecializationKind : 3;
+  SourceLocation PointOfInstantiation;
+  SourceLocation Location;
+  const DeclContext *LexicalDC;
+
+  bool operator==(const SpecializationFootprint &O) const {
+    return SpecializationKind == O.SpecializationKind &&
+           PointOfInstantiation == O.PointOfInstantiation &&
+           Location == O.Location && LexicalDC == O.LexicalDC;
+  }
+  bool operator!=(const SpecializationFootprint &O) const {
+    return !(*this == O);
+  }
+
+  void update(const ClassTemplateSpecializationDecl &Live) {
+    SpecializationKind = Live.getSpecializationKind();
+    PointOfInstantiation = Live.getPointOfInstantiation();
+    Location = Live.getLocation();
+    LexicalDC = Live.getLexicalDeclContext();
+  }
+
+  void restore(ClassTemplateSpecializationDecl &Live) const {
+    Live.setSpecializationKind(
+        static_cast<TemplateSpecializationKind>(SpecializationKind));
+    Live.setPointOfInstantiation(PointOfInstantiation);
+    Live.setLocation(Location);
+    Live.setLexicalDeclContext(const_cast<DeclContext *>(LexicalDC));
+  }
+};
+
+// Stores the state of a variable template specialization. Tracks its
+// specialization kind and point of instantiation so the state can be
+// compared and restored.
+struct VarSpecializationFootprint {
+  unsigned SpecializationKind : 3;
+  SourceLocation PointOfInstantiation;
+
+  bool operator==(const VarSpecializationFootprint &O) const {
+    return SpecializationKind == O.SpecializationKind &&
+           PointOfInstantiation == O.PointOfInstantiation;
+  }
+  bool operator!=(const VarSpecializationFootprint &O) const {
+    return !(*this == O);
+  }
+
+  void update(const VarTemplateSpecializationDecl &Live) {
+    SpecializationKind = Live.getSpecializationKind();
+    PointOfInstantiation = Live.getPointOfInstantiation();
+  }
+
+  void restore(VarTemplateSpecializationDecl &Live) const {
+    Live.setSpecializationKind(
+        static_cast<TemplateSpecializationKind>(SpecializationKind));
+    Live.setPointOfInstantiation(PointOfInstantiation);
+  }
+};
+
+// Stores the state of a function template specialization. Tracks its
+// specialization kind, point of instantiation, source location, lexical
+// declaration context, and constexpr state so the state can be compared
+// and restored.
+struct FunctionSpecializationFootprint {
+  unsigned SpecializationKind : 3;
+  SourceLocation PointOfInstantiation;
+  SourceLocation Location;
+  const DeclContext *LexicalDC;
+  ConstexprSpecKind ConstexprKind;
+
+  bool operator==(const FunctionSpecializationFootprint &O) const {
+    return SpecializationKind == O.SpecializationKind &&
+           PointOfInstantiation == O.PointOfInstantiation &&
+           Location == O.Location && LexicalDC == O.LexicalDC &&
+           ConstexprKind == O.ConstexprKind;
+  }
+  bool operator!=(const FunctionSpecializationFootprint &O) const {
+    return !(*this == O);
+  }
+
+  void update(const FunctionDecl &Live) {
+    SpecializationKind = Live.getTemplateSpecializationKind();
+    PointOfInstantiation = Live.getPointOfInstantiation();
+    Location = Live.getLocation();
+    LexicalDC = Live.getLexicalDeclContext();
+    ConstexprKind = Live.getConstexprKind();
+  }
+
+  void restore(FunctionDecl &Live) const {
+    Live.setTemplateSpecializationKind(
+        static_cast<TemplateSpecializationKind>(SpecializationKind),
+        PointOfInstantiation);
+    Live.setLocation(Location);
+    Live.setLexicalDeclContext(const_cast<DeclContext *>(LexicalDC));
+    Live.setConstexprKind(ConstexprKind);
+  }
+};
+
+// Stores the state of an ordinary class member created from a class template
+// instantiation. Tracks its specialization kind, point of instantiation, and
+// source location so the state can be compared and restored.
+struct MemberSpecializationFootprint {
+  unsigned SpecializationKind : 3;
+  SourceLocation PointOfInstantiation;
+  SourceLocation Location;
+
+  bool operator==(const MemberSpecializationFootprint &O) const {
+    return SpecializationKind == O.SpecializationKind &&
+           PointOfInstantiation == O.PointOfInstantiation &&
+           Location == O.Location;
+  }
+  bool operator!=(const MemberSpecializationFootprint &O) const {
+    return !(*this == O);
+  }
+
+  template <typename OwnerT> void update(const OwnerT &Live) {
+    const MemberSpecializationInfo *MSI = Live.getMemberSpecializationInfo();
+    SpecializationKind = MSI->getTemplateSpecializationKind();
+    PointOfInstantiation = MSI->getPointOfInstantiation();
+    Location = Live.getLocation();
+  }
+
+  template <typename OwnerT> void restore(OwnerT &Live) const {
+    MemberSpecializationInfo *MSI = Live.getMemberSpecializationInfo();
+    MSI->setTemplateSpecializationKind(
+        static_cast<TemplateSpecializationKind>(SpecializationKind));
+    MSI->setPointOfInstantiation(PointOfInstantiation);
+    Live.setLocation(Location);
+  }
+};
+
+class ASTStateReader {
+public:
+  static DefinitionDataFootprint *
+  createDefinitionDataFootprint(const ASTContext &Ctx, const CXXRecordDecl &RD);
+
+  static bool compareDefinitionDataFootprint(const DefinitionDataFootprint &FP,
+                                             const CXXRecordDecl &RD);
+
+  static void restoreDefinitionDataFootprint(const DefinitionDataFootprint &FP,
+                                             CXXRecordDecl &RD);
+
+  static SpecializationFootprint *
+  createSpecializationFootprint(const ASTContext &Ctx,
+                                const ClassTemplateSpecializationDecl &Spec);
+
+  static bool
+  compareSpecializationFootprint(const SpecializationFootprint &FP,
+                                 const ClassTemplateSpecializationDecl &Spec) {
+    SpecializationFootprint Live;
+    Live.update(Spec);
+    return FP == Live;
+  }
+  static void
+  restoreSpecializationFootprint(const SpecializationFootprint &FP,
+                                 ClassTemplateSpecializationDecl &Spec) {
+    FP.restore(Spec);
+  }
+
+  static VarSpecializationFootprint *
+  createVarSpecializationFootprint(const ASTContext &Ctx,
+                                   const VarTemplateSpecializationDecl &Spec);
+
+  static bool
+  compareVarSpecializationFootprint(const VarSpecializationFootprint &FP,
+                                    const VarTemplateSpecializationDecl &Spec) {
+    VarSpecializationFootprint Live;
+    Live.update(Spec);
+    return FP == Live;
+  }
+  static void
+  restoreVarSpecializationFootprint(const VarSpecializationFootprint &FP,
+                                    VarTemplateSpecializationDecl &Spec) {
+    FP.restore(Spec);
+  }
+
+  static FunctionSpecializationFootprint *
+  createFunctionSpecializationFootprint(const ASTContext &Ctx,
+                                        const FunctionDecl &FD);
+
+  static bool compareFunctionSpecializationFootprint(
+      const FunctionSpecializationFootprint &FP, const FunctionDecl &FD);
+
+  static void restoreFunctionSpecializationFootprint(
+      const FunctionSpecializationFootprint &FP, FunctionDecl &FD) {
+    FP.restore(FD);
+  }
+
+  template <typename OwnerT>
+  static MemberSpecializationFootprint *
+  createMemberSpecializationFootprint(const ASTContext &Ctx, const OwnerT &D);
+
+  template <typename OwnerT>
+  static bool
+  compareMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
+                                       const OwnerT &D);
+  template <typename OwnerT>
+  static void
+  restoreMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
+                                       OwnerT &D);
+
+  static void restoreDefinitionAndRevertDC(CXXRecordDecl &RD);
+
+  static void revertDefinitionArrival(Decl &D);
+
+  static const void *getRawCommonPtr(const RedeclarableTemplateDecl &RT);
+
+  static bool isTemplateCanonInjectedTSTValid(const ClassTemplateDecl *CTD);
+
+  static void resetTemplateCommonBase(RedeclarableTemplateDecl &RT);
+
+  static void resetCanonInjectedTST(ClassTemplateDecl &CTD);
+
+  static const Type *getRawTypeForDecl(const TypeDecl *TD);
+
+  static void resetTypeForDecl(TypeDecl *TD);
+};
+
+template <typename DataT> struct Snapshot {
+  PTUID ID;
+  DataT *Data;
+};
+
+template <typename DataT> class StateAwareChain {
+  llvm::SmallVector<Snapshot<DataT>, 4> History;
+
+public:
+  bool empty() const { return History.empty(); }
+
+  const DataT *mostRecent() const {
+    return History.empty() ? nullptr : History.back().Data;
+  }
+
+  std::optional<PTUID> mostRecentID() const {
+    return History.empty() ? std::nullopt
+                           : std::optional<PTUID>(History.back().ID);
+  }
+
+  std::optional<PTUID> oldestID() const {
+    return History.empty() ? std::nullopt
+                           : std::optional<PTUID>(History.front().ID);
+  }
+
+  void commit(PTUID ID, DataT *Fresh) {
+    if (!History.empty() && *History.back().Data == *Fresh)
+      return;
+    assert(History.back().ID != ID);
+    History.push_back(Snapshot<DataT>{ID, Fresh});
+  }
+
+  const DataT *getPrevious(PTUID ID) const {
+    for (auto It = History.rbegin(); It != History.rend(); ++It)
+      if (It->ID < ID)
+        return It->Data;
+    return nullptr;
+  }
+
+  /// Pure removal: drop every entry with ID >= \p ID (LIFO). Never
+  /// restores anything -- callers must read mostRecent()/getPrevious()
+  /// themselves first if they need the value about to be dropped.
+  void removeFrom(PTUID ID) {
+    while (!History.empty() && History.back().ID >= ID)
+      History.pop_back(); // no delete.
+  }
+};
+
+// struct UnusedTrait {};
+
+using RecordDeclDefinitionDataChain = StateAwareChain<DefinitionDataFootprint>;
+using SpecializationChain = StateAwareChain<SpecializationFootprint>;
+using VarSpecializationChain = StateAwareChain<VarSpecializationFootprint>;
+using FunctionSpecializationChain =
+    StateAwareChain<FunctionSpecializationFootprint>;
+// One chain type shared by all four MemberSpecializationInfo-backed owner
+// kinds (Function/Var/Record/Enum) -- the footprint shape is identical
+// across all four (see MemberSpecializationFootprint's own comment), so
+// unlike the three chains above, a single alias is enough; what varies is
+// only the DenseMap key type in IncrementalStateTracker
+// (FunctionDecl*/VarDecl*/ CXXRecordDecl*/EnumDecl*), one map per owner kind.
+using MemberSpecializationChain =
+    StateAwareChain<MemberSpecializationFootprint>;
+
+class PTUCheckpointLedger {
+  llvm::SmallVector<llvm::SlabCheckPoint, 16> CheckpointBeforePTU;
+
+public:
+  /// Called once, right before parsing PTU \p ID begins.
+  void recordCheckpoint(PTUID ID, llvm::SlabCheckPoint CP) {
+    assert(ID == CheckpointBeforePTU.size() &&
+           "PTUs must be recorded in order");
+    CheckpointBeforePTU.push_back(CP);
+  }
+
+  /// \return the PTU that allocated \p Ptr, or std::nullopt if \p Ptr
+  /// predates the oldest recorded checkpoint.
+  ///
+  /// Walks newest-to-oldest: checkpoints only ever move forward for state
+  /// that has survived (committed PTUs are never rewound), so the first
+  /// checkpoint for which \p Ptr is "after" is the PTU that produced it.
+  std::optional<PTUID> attribute(const ASTContext &Ctx, const void *Ptr) const {
+    for (PTUID ID = CheckpointBeforePTU.size(); ID-- > 0;) {
+      if (Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]))
+        return ID;
+    }
+    return std::nullopt;
+  }
+
+  inline std::optional<PTUID> attributeByAddress(const ASTContext &Ctx,
+                                                 const void *Ptr) {
+    // Binary search for the largest ID whose checkpoint Ptr is after -- i.e.
+    // the newest PTU boundary this address was allocated on or past.
+    size_t Lo = 0, Hi = CheckpointBeforePTU.size();
+    std::optional<PTUID> Result;
+    while (Lo < Hi) {
+      size_t Mid = Lo + (Hi - Lo) / 2;
+      if (Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[Mid])) {
+        Result = static_cast<PTUID>(Mid);
+        Lo = Mid + 1; // still after Mid's checkpoint -- look for a later one
+      } else {
+        Hi = Mid; // not even after Mid -- must be before it
+      }
+    }
+    return Result;
+  }
+
+  bool predatesPTU(const ASTContext &Ctx, const void *Ptr, PTUID ID) const {
+    assert(ID < CheckpointBeforePTU.size());
+    return !Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
+  }
+
+  bool isFromThisPTU(const ASTContext &Ctx, const void *Ptr, PTUID ID) const {
+    return Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
+  }
+
+  /// Drop checkpoints from \p ID onward.
+  void undoFrom(PTUID ID) {
+    if (ID < CheckpointBeforePTU.size())
+      CheckpointBeforePTU.resize(ID);
+  }
+};
+
+/// need to handle this     TagDeclBitfields TagDeclBits;
+
+template <typename ValueT> struct FieldMutation {
+  PTUID ID;
+  ValueT OldValue;
+};
+
+template <typename OwnerT, typename ValueT> class FieldMutationChain {
+  llvm::DenseMap<const OwnerT *, llvm::SmallVector<FieldMutation<ValueT>, 2>>
+      Log;
+
+public:
+  void noteMutation(PTUID ID, const OwnerT *Owner, ValueT OldValue) {
+    auto &Entries = Log[Owner];
+    if (!Entries.empty() && Entries.back().ID == ID)
+      return;
+    Entries.push_back(FieldMutation<ValueT>{ID, OldValue});
+  }
+
+  const ValueT *mostRecent(const OwnerT *Owner) const {
+    auto It = Log.find(Owner);
+    return (It == Log.end() || It->second.empty())
+               ? nullptr
+               : &It->second.back().OldValue;
+  }
+
+  std::optional<PTUID> mostRecentID(const OwnerT *Owner) const {
+    auto It = Log.find(Owner);
+    if (It == Log.end())
+      return std::nullopt;
+    auto &History = It->second;
+    return History.empty() ? std::nullopt
+                           : std::optional<PTUID>(History.back().ID);
+  }
+
+  std::optional<PTUID> oldestID(const OwnerT *Owner) const {
+    auto It = Log.find(Owner);
+    if (It == Log.end())
+      return std::nullopt;
+    auto &History = It->second;
+    return History.empty() ? std::nullopt
+                           : std::optional<PTUID>(History.front().ID);
+  }
+
+  template <typename FnT> void forEachOwnerSince(PTUID ID, FnT &&Fn) const {
+    for (auto &Entry : Log)
+      if (!Entry.second.empty() && Entry.second.back().ID >= ID)
+        Fn(Entry.first);
+  }
+
+  void removeFrom(PTUID ID) {
+    llvm::SmallVector<OwnerT *> ToErase;
+    for (auto &Entry : Log) {
+      auto &Entries = Entry.second;
+      while (!Entries.empty() && Entries.back().ID >= ID)
+        Entries.pop_back();
+      if (Entries.empty())
+        ToErase.push_back(Entry.first);
+    }
+    for (const OwnerT *O : ToErase)
+      Log.erase(O);
+  }
+
+  void forget(const OwnerT *Owner) { Log.erase(Owner); }
+};
+
+// struct FunctionTypeEntry {
+//   QualType Type;
+//   QualType OverridingType;
+// };
+using FunctionExceptionSpecChain = FieldMutationChain<FunctionDecl, QualType>;
+using TypeForDeclChain = FieldMutationChain<TagDecl, const Type *>;
+// using CanonInjectedTSTChain =
+//     FieldMutationChain<ClassTemplateDecl, CanQualType>;
+
+struct MutationRecord {
+  enum class DeclShape : uint16_t {
+    None = 0,
+    // Base shapes -- what the decl fundamentally IS. Exactly one is set.
+    Class = 1 << 0,    // CXXRecordDecl/TagDecl: DefinitionData, TypeForDecl
+    Function = 1 << 1, // FunctionDecl: exception spec, deduced return, body
+    Var = 1 << 2,      // VarDecl: cached constant-eval result
+    Enum = 1 << 3,     // EnumDecl: TypeForDecl
+    Template = 1 << 4, // RedeclarableTemplateDecl: spec list grows
+    Typedef = 1 << 5,  // TypedefDecl/TypeAliasDecl: TypeForDecl.
+  };
+
+  enum class MutationKind : uint32_t {
+    // ---- Common ----
+    DefinitionInstantiate = 1 << 0, // Class, Function, Var
+    SpecInfo = 1 << 1,              // Class, Function, Var  (Spec)
+    MemberSpecInfo = 1 << 2,        // Class, Function, Var, Enum (Member)
+    TypeForDecl = 1 << 3,           // Class, Enum
+
+    // ---- Shape-specific ----
+    DefinitionData = 1 << 8,       // Class only
+    ExceptionSpec = 1 << 9,        // Function only
+    DeducedReturnType = 1 << 10,   // Function only
+    EvaluatedValue = 1 << 11,      // Var only
+    SpecializationAdded = 1 << 12, // Template only
+
+    TemplateCommon = 1 << 4,   // Template CommonBase
+    CanonInjectedTST = 1 << 5, // Template CommonBase Type
+    None = 1 << 24,
+  };
+
+  DeclShape S = DeclShape::None;
+  uint32_t MutationType = 0; // what this decl can ever have
+
+  void add(MutationKind K) { MutationType |= uint32_t(K); }
+  void add(uint32_t K) { MutationType |= uint32_t(K); }
+  bool has(MutationKind K) const { return MutationType & uint32_t(K); }
+  void clear(MutationKind K) { MutationType &= ~uint32_t(K); }
+};
+
+using DeclShape = MutationRecord::DeclShape;
+using MutationType = MutationRecord::MutationKind;
+
+class PTUMutationActions;
+
+struct PTUStateInfo {
+  PTUID ID;
+  const TranslationUnitDecl *ThisTU; // current info
+
+  llvm::MapVector<const Decl *, MutationRecord> Mutations;
+
+  // struct CreationRecord {
+  //   DeclShape S;
+  //   CreatedType Type;
+  // };
+
+  // llvm::MapVector<const Decl *, CreationRecord> CreatedDecls;
+  llvm::SmallPtrSet<const Decl *, 4> ImplicitDecls;
+
+  /// here touched info mean other this belongs to other PTUs;
+  llvm::SmallPtrSet<const DeclContext *, 4> TouchedDC;
+
+  template <typename KindT>
+  void noteMutated(const Decl *D, DeclShape S, KindT K) {
+    if (!D->isDefinedOutsideFunctionOrMethod())
+      return;
+    auto [It, Inserted] = Mutations.try_emplace(D);
+    if (Inserted) {
+      assert(It->second.S == S && "same decl noted under two different shapes");
+      It->second.S = S;
+    }
+    It->second.add(K);
+  }
+
+  void verifyMutations();
+
+  // False until this PTU is actually committed.
+  // PTUMutationActions uses this to decide what "undo" should do:
+  // - If this PTU was never committed, nothing was added to the chain,
+  //   so we just restore the last PTU that was committed.
+  // - If this PTU was already committed (for example, by using %undo),
+  //   first remove the entries created by this PTU, then restore the
+  //   state from before this PTU was added.
+  bool Commited = false;
+};
+
+//===----------------------------------------------------------------------===//
+// SweepTracker -- Tracks declarations whose mutations cannot be reported
+// directly by an ASTMutationListener.
+//
+// Each Decl is tracked with the hidden mutation kinds that are still possible.
+// At commit time, sweep() checks the tracked kinds to find mutations made by
+// the current PTU. Once a mutation kind is confirmed or can no longer happen,
+// it is removed from tracking.
+//
+// During restore, a mutation kind can be tracked again so its mutation sites
+// can be restored when needed.
+//===----------------------------------------------------------------------===//
+class SweepTracker {
+  llvm::DenseMap<const Decl *, uint32_t> Active;
+
+public:
+  void track(const Decl *D, uint32_t HiddenBits) {
+    if (HiddenBits)
+      Active[D] |= HiddenBits;
+  }
+
+  bool isTrackedFor(const Decl *D, uint32_t Flag) const {
+    auto It = Active.find(D);
+    return It != Active.end() && (It->second & Flag);
+  }
+
+  void settle(const Decl *D, uint32_t Flag) {
+    auto It = Active.find(D);
+    if (It == Active.end())
+      return;
+    It->second &= ~Flag;
+    if (!It->second)
+      Active.erase(It);
+  }
+
+  // Call at commit(ID) time. OnConfirmed is invoked as
+  // (const Decl *D, DeclShape S, uint32_t ConfirmedKinds) for every decl
+  // that had something newly confirmed this sweep.
+  template <typename OnConfirmedFn>
+  void sweep(PTUID ID, OnConfirmedFn &&OnConfirmed);
+};
+
+//===----------------------------------------------------------------------===//
+// DeclLinkedState / LinkedDeclNodeGenerator
+//
+// These nodes keep the footprint-chain state associated with a Decl.
+// Each Decl kind has its own node type so the node only contains state that
+// is valid for that kind. DeclLinkedState then uses the node type to identify
+// which kind of Decl it belongs to.
+//
+// The generator owns these nodes and their chains and reuses them through
+// per-type free lists. This is intentional: nodes need to be released and
+// reused, so they use normal heap allocation instead of ASTContext's
+// bump allocator.
+//
+// The goal is to keep the linked state small, type-safe, and reusable without
+// adding fields for states that a particular Decl can never have.
+//===----------------------------------------------------------------------===//
+struct CXXClassDeclNode {
+  PTUID OriginID;
+  RecordDeclDefinitionDataChain *DefData = nullptr;
+  llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *> Spec;
+  CXXClassDeclNode *Next = nullptr; // free-list link, meaningless off the list
+};
+
+struct VarDeclNode {
+  PTUID OriginID;
+  llvm::PointerUnion<VarSpecializationChain *, MemberSpecializationChain *>
+      Spec;
+  VarDeclNode *Next = nullptr;
+};
+
+struct FunctionDeclNode {
+  PTUID OriginID;
+  llvm::PointerUnion<FunctionSpecializationChain *, MemberSpecializationChain *>
+      Spec;
+  FunctionDeclNode *Next = nullptr;
+};
+
+struct EnumDeclNode {
+  PTUID OriginID;
+  MemberSpecializationChain *MemberSpec =
+      nullptr; // the only thing an enum can ever have
+  EnumDeclNode *Next = nullptr;
+};
+
+using DeclLinkedState = llvm::PointerUnion<CXXClassDeclNode *, VarDeclNode *,
+                                           FunctionDeclNode *, EnumDeclNode *>;
+
+/// Intrusive free-list pool for the four node types above. Released nodes
+/// are kept for reuse instead of being immediately deleted. The pool is
+/// bounded by Capacity; once it is full, additional released nodes are
+/// deleted instead of being kept. This keeps memory usage bounded while
+/// still allowing freed nodes to be reused.
+
+template <typename T> class NodePool {
+  T *FreeList = nullptr;
+  unsigned FreeCount = 0;
+  unsigned Capacity;
+
+public:
+  explicit NodePool(unsigned Capacity = 64) : Capacity(Capacity) {}
+
+  ~NodePool() {
+    while (FreeList) {
+      T *Dead = FreeList;
+      FreeList = FreeList->Next;
+      delete Dead;
+    }
+  }
+  /// Hands back a reset (all-default) node -- recycled if one is free,
+  /// freshly allocated otherwise.
+  T *acquire() {
+    if (T *N = FreeList) {
+      FreeList = N->Next;
+      --FreeCount;
+      *N = T();
+      return N;
+    }
+    return new T();
+  }
+  /// Takes ownership back. Kept for a future acquire() to hand out again
+  /// if the free list is under capacity; reclaimed for real (delete)
+  /// otherwise, so this pool never holds more than Capacity dead nodes.
+  void release(T *N) {
+    if (FreeCount >= Capacity) {
+      delete N;
+      return;
+    }
+    N->Next = FreeList;
+    FreeList = N;
+    ++FreeCount;
+  }
+};
+
+/// Pool for reusing chain objects without modifying StateAwareChain.
+/// Released chains are kept for reuse, and the pool is bounded by Capacity
+/// so unused chains do not cause unbounded memory growth.
+template <typename ChainT> class ChainPool {
+  llvm::SmallVector<ChainT *, 8> Free;
+  unsigned Capacity;
+
+public:
+  explicit ChainPool(unsigned Capacity = 64) : Capacity(Capacity) {}
+
+  ~ChainPool() {
+    for (ChainT *C : Free)
+      delete C;
+  }
+
+  ChainT *acquire() {
+    if (!Free.empty()) {
+      ChainT *C = Free.pop_back_val();
+      *C = ChainT();
+      return C;
+    }
+    return new ChainT();
+  }
+  /// Same capacity rule as NodePool::release() -- reclaimed for real once
+  /// Free is at capacity, rather than growing without bound.
+  void release(ChainT *C) {
+    if (Free.size() >= Capacity) {
+      delete C;
+      return;
+    }
+    Free.push_back(C);
+  }
+};
+
+class LinkedDeclNodeGenerator {
+  NodePool<CXXClassDeclNode> RecordNodes;
+  NodePool<VarDeclNode> VarNodes;
+  NodePool<FunctionDeclNode> FunctionNodes;
+  NodePool<EnumDeclNode> EnumNodes;
+
+  ChainPool<RecordDeclDefinitionDataChain> DefDataChains;
+  ChainPool<SpecializationChain> ClassSpecChains;
+  ChainPool<VarSpecializationChain> VarSpecChains;
+  ChainPool<FunctionSpecializationChain> FunctionSpecChains;
+  ChainPool<MemberSpecializationChain> MemberSpecChains;
+
+public:
+  CXXClassDeclNode *acquireRecordNode() { return RecordNodes.acquire(); }
+  VarDeclNode *acquireVarNode() { return VarNodes.acquire(); }
+  FunctionDeclNode *acquireFunctionNode() { return FunctionNodes.acquire(); }
+  EnumDeclNode *acquireEnumNode() { return EnumNodes.acquire(); }
+
+  RecordDeclDefinitionDataChain *acquireDefDataChain() {
+    return DefDataChains.acquire();
+  }
+  SpecializationChain *acquireClassSpecChain() {
+    return ClassSpecChains.acquire();
+  }
+  VarSpecializationChain *acquireVarSpecChain() {
+    return VarSpecChains.acquire();
+  }
+  FunctionSpecializationChain *acquireFunctionSpecChain() {
+    return FunctionSpecChains.acquire();
+  }
+  MemberSpecializationChain *acquireMemberSpecChain() {
+    return MemberSpecChains.acquire();
+  }
+
+  // One release() name per type, dispatched by overload resolution --
+  // the caller doesn't need to know which internal pool a given pointer
+  // belongs to.
+  void release(CXXClassDeclNode *N) { RecordNodes.release(N); }
+  void release(VarDeclNode *N) { VarNodes.release(N); }
+  void release(FunctionDeclNode *N) { FunctionNodes.release(N); }
+  void release(EnumDeclNode *N) { EnumNodes.release(N); }
+  void release(RecordDeclDefinitionDataChain *C) { DefDataChains.release(C); }
+  void release(SpecializationChain *C) { ClassSpecChains.release(C); }
+  void release(VarSpecializationChain *C) { VarSpecChains.release(C); }
+  void release(FunctionSpecializationChain *C) {
+    FunctionSpecChains.release(C);
+  }
+  void release(MemberSpecializationChain *C) { MemberSpecChains.release(C); }
+};
+
+enum class LangMode : uint8_t {
+  C,   // no DefinitionData, no templates, no implicit special members
+  CXX, // full set
+};
+
+// Overall flow:
+// - Track changes made by other PTUs so they can be applied to the relevant
+//   chains/mutations when those PTUs are committed.
+// - On rollback of a committed PTU, remove the entries created by that PTU
+//   and restore the state that existed before it.
+// - On rollback of a PTU that was never committed, restore the chain to its
+//   current mostRecent() state, since this PTU never added anything to it.
+// - For the current PTU, collect newly added declarations that are
+//   modifiable/visible to other PTUs. Local-only declarations are not exposed.
+//
+// In short, this keeps the shared declaration state in sync across PTUs while
+// keeping declarations that are local to the current PTU private.
+class IncrementalStateTracker {
+private:
+  ASTContext &Ctx;
+  LangMode Mode;
+  PTUID NextID = 0;
+  PTUID CurID = NextID;
+  PTUCheckpointLedger PTUSlabCheckpoints;
+
+  mutable llvm::DenseMap<PTUID, PTUStateInfo> PTUStateInfos;
+
+  // Stores all declaration footprint state in one map instead of maintaining
+  // separate maps for each declaration/specialization kind. The generator
+  // creates the appropriate node and chain for each Decl.
+  mutable LinkedDeclNodeGenerator Generator;
+  mutable llvm::DenseMap<const Decl *, DeclLinkedState> LinkedDecls;
+
+  // Tracks hidden mutations for all supported Decl kinds in one shared tracker,
+  // so they can be restored correctly during rollback.
+  SweepTracker HiddenMutationTracker;
+
+  FunctionExceptionSpecChain FunctionTypeMutations;
+  TypeForDeclChain TagdeclInfos;
+  // CanonInjectedTSTChain;
+
+  friend class PTUMutationActions;
+
+  SweepTracker &getHiddenMutationTracker() { return HiddenMutationTracker; }
+
+  /// Get-or-create the CXXClassDeclNode backing Canon, acquiring one from
+  /// the generator on first use. The only place a CXXClassDeclNode is
+  /// created for this map.
+  CXXClassDeclNode &recordNodeFor(const CXXRecordDecl *Canon) const {
+    DeclLinkedState &Info = LinkedDecls[Canon];
+    if (Info.isNull())
+      Info = Generator.acquireRecordNode();
+    return *cast<CXXClassDeclNode *>(Info);
+  }
+  VarDeclNode &varNodeFor(const VarDecl *Canon) const {
+    DeclLinkedState &Info = LinkedDecls[Canon];
+    if (Info.isNull())
+      Info = Generator.acquireVarNode();
+    return *cast<VarDeclNode *>(Info);
+  }
+  FunctionDeclNode &functionNodeFor(const FunctionDecl *Canon) const {
+    DeclLinkedState &Info = LinkedDecls[Canon];
+    if (Info.isNull())
+      Info = Generator.acquireFunctionNode();
+    return *cast<FunctionDeclNode *>(Info);
+  }
+  EnumDeclNode &enumNodeFor(const EnumDecl *Canon) const {
+    DeclLinkedState &Info = LinkedDecls[Canon];
+    if (Info.isNull())
+      Info = Generator.acquireEnumNode();
+    return *cast<EnumDeclNode *>(Info);
+  }
+
+  RecordDeclDefinitionDataChain &chainFor(const CXXRecordDecl *RD) {
+    CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+    if (!Node.DefData)
+      Node.DefData = Generator.acquireDefDataChain();
+    return *Node.DefData;
+  }
+
+  SpecializationChain &chainFor(const ClassTemplateSpecializationDecl *Spec) {
+    CXXClassDeclNode &Node = recordNodeFor(
+        cast<ClassTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
+    auto *Chain = Node.Spec.dyn_cast<SpecializationChain *>();
+    if (!Chain) {
+      Chain = Generator.acquireClassSpecChain();
+      Node.Spec = Chain;
+    }
+    return *Chain;
+  }
+
+  VarSpecializationChain &chainFor(const VarTemplateSpecializationDecl *Spec) {
+    VarDeclNode &Node = varNodeFor(
+        cast<VarTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
+    auto *Chain = Node.Spec.dyn_cast<VarSpecializationChain *>();
+    if (!Chain) {
+      Chain = Generator.acquireVarSpecChain();
+      Node.Spec = Chain;
+    }
+    return *Chain;
+  }
+
+  FunctionSpecializationChain &chainFor(const FunctionDecl *FD) {
+    FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
+    auto *Chain = Node.Spec.dyn_cast<FunctionSpecializationChain *>();
+    if (!Chain) {
+      Chain = Generator.acquireFunctionSpecChain();
+      Node.Spec = Chain;
+    }
+    return *Chain;
+  }
+
+  MemberSpecializationChain &memberSpecChainFor(const FunctionDecl *FD) {
+    FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
+    auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
+    if (!Chain) {
+      Chain = Generator.acquireMemberSpecChain();
+      Node.Spec = Chain;
+    }
+    return *Chain;
+  }
+  MemberSpecializationChain &memberSpecChainFor(const VarDecl *VD) {
+    VarDeclNode &Node = varNodeFor(VD->getCanonicalDecl());
+    auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
+    if (!Chain) {
+      Chain = Generator.acquireMemberSpecChain();
+      Node.Spec = Chain;
+    }
+    return *Chain;
+  }
+  MemberSpecializationChain &memberSpecChainFor(const CXXRecordDecl *RD) {
+    CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+    auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
+    if (!Chain) {
+      Chain = Generator.acquireMemberSpecChain();
+      Node.Spec = Chain;
+    }
+    return *Chain;
+  }
+  MemberSpecializationChain &memberSpecChainFor(const EnumDecl *ED) {
+    EnumDeclNode &Node = enumNodeFor(ED->getCanonicalDecl());
+    if (!Node.MemberSpec)
+      Node.MemberSpec = Generator.acquireMemberSpecChain();
+    return *Node.MemberSpec;
+  }
+
+  RecordDeclDefinitionDataChain *getChainFor(const CXXRecordDecl *RD) const {
+    CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+    return Node.DefData;
+  }
+
+  SpecializationChain *
+  getChainFor(const ClassTemplateSpecializationDecl *Spec) const {
+    CXXClassDeclNode &Node = recordNodeFor(
+        cast<ClassTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
+    return Node.Spec.dyn_cast<SpecializationChain *>();
+  }
+
+  VarSpecializationChain *
+  getChainFor(const VarTemplateSpecializationDecl *Spec) const {
+    VarDeclNode &Node = varNodeFor(
+        cast<VarTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
+    return Node.Spec.dyn_cast<VarSpecializationChain *>();
+  }
+
+  FunctionSpecializationChain *getChainFor(const FunctionDecl *FD) const {
+    FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
+    return Node.Spec.dyn_cast<FunctionSpecializationChain *>();
+  }
+
+  MemberSpecializationChain *
+  getMemberSpecChainFor(const FunctionDecl *FD) const {
+    FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
+    return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+  }
+  MemberSpecializationChain *getMemberSpecChainFor(const VarDecl *VD) const {
+    VarDeclNode &Node = varNodeFor(VD->getCanonicalDecl());
+    return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+  }
+  MemberSpecializationChain *
+  getMemberSpecChainFor(const CXXRecordDecl *RD) const {
+    CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+    return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+  }
+  MemberSpecializationChain *getMemberSpecChainFor(const EnumDecl *ED) const {
+    EnumDeclNode &Node = enumNodeFor(ED->getCanonicalDecl());
+    return Node.MemberSpec;
+  }
+
+public:
+  IncrementalStateTracker(ASTContext &Ctx, LangMode M) : Ctx(Ctx), Mode(M) {}
+
+  void beginPTU(llvm::SlabCheckPoint CP) {
+    CurID = NextID;
+    PTUSlabCheckpoints.recordCheckpoint(CurID, CP);
+    PTUStateInfos.try_emplace(CurID, PTUStateInfo{CurID});
+    ++NextID;
+  }
+
+  PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
+  bool isFromThisPTU(const void *Ptr, PTUID ID) {
+    return PTUSlabCheckpoints.isFromThisPTU(Ctx, Ptr, ID);
+  }
+
+  PTUStateInfo &current() const {
+    assert(NextID > 0 && "no PTU has been started");
+    auto It = PTUStateInfos.find(CurID);
+    assert(It != PTUStateInfos.end());
+    return It->second;
+  }
+
+  // Pops this PTU's own tail entry (if any) from *Field. If the chain is
+  // now empty -- meaning everything it ever held belonged to the PTU being
+  // rolled back -- releases it back to its pool and nulls the pointer.
+  // Returns whether the chain still has surviving (earlier-PTU) history.
+  template <typename ChainT>
+  static bool rollbackChainField(ChainT *&Field, PTUID ID,
+                                 LinkedDeclNodeGenerator &Gen);
+
+  static bool rollbackRecordSpec(
+      llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *>
+          &Spec,
+      PTUID ID, LinkedDeclNodeGenerator &Gen);
+  void removeLinkedDecl(const Decl *D, PTUID ID);
+  // just only remove entries from current();
+  void undoLastEntries();
+};
+
+class PTUMutationActions {
+private:
+  IncrementalStateTracker &Tracker;
+
+public:
+  explicit PTUMutationActions(IncrementalStateTracker &Tracker)
+      : Tracker(Tracker) {}
+
+  /// Every mutation this PTU recorded for a class-shaped decl, applied in
+  /// dependency order: definition data first (later steps read the completed
+  /// definition), then the type cache, then specialization footprints, then
+  /// lazily-completed members last (they can add members that the steps above
+  /// would otherwise have missed).
+  ///
+  /// Kinds are a bitmask, not alternatives -- one class can legitimately have
+  /// several set in a single PTU (e.g. a specialization that also had its
+  /// definition data completed), so these are sequential checks, not a switch.
+  void commitClassFamily(PTUID ID, const CXXRecordDecl *RD, MutationRecord &Rec,
+                         bool IsNew);
+
+  /// Every mutation this PTU recorded for a function-shaped decl, or -- when
+  /// CR is non-null -- the baseline seed for one this PTU created.
+  ///
+  /// Kinds are a bitmask, not alternatives: one function can have its
+  /// exception spec resolved AND its body instantiated in the same PTU, so
+  /// these are sequential checks rather than a switch.
+  ///
+  /// Order matters: type-affecting mutations (exception spec, deduced return)
+  /// come first because the specialization footprint below reads the
+  /// function's type; body instantiation comes last because it can only
+  /// happen once everything about the signature is settled.
+  void commitFunctionFamily(PTUID ID, const FunctionDecl *FD,
+                            MutationRecord &Rec, bool IsNew);
+
+  /// Every mutation this PTU recorded for a var-shaped decl, or -- when CR is
+  /// non-null -- the baseline seed for one this PTU created.
+  ///
+  /// Ordering: initializer instantiation first (it produces the expression
+  /// that constant evaluation later consumes), then the cached evaluated
+  /// value, then specialization/member footprints which read both.
+  void commitVarFamily(PTUID ID, const VarDecl *VD, MutationRecord &Rec,
+                       bool IsNew);
+
+  /// Every mutation this PTU recorded for an enum-shaped decl, or -- when CR
+  /// is non-null -- the baseline seed for one this PTU created.
+  ///
+  /// The smallest family: enums have no specialization category (there is no
+  /// such thing as an enum template), no deferred bodies, and no members with
+  /// independent mutable state -- EnumConstantDecls live and die with the
+  /// EnumDecl, so they are not separately tracked.
+  void commitEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec,
+                        bool IsNew);
+
+  // static DeclShape classifyShape(const Decl *D);
+
+  // class DeclStateCommitProxy;
+
+  // class DeclStateRestoreProxy;
+
+  template <typename DeclStateProxyT>
+  void walkDecls(const DeclContext *DC, DeclStateProxyT &Proxy);
+
+  // static uint32_t classifyPossibleKinds(DeclShape S, const Decl *D);
+
+  /// Given a Decl already known to be DeclShape S with FlaggedKinds set
+  /// (more than one bit at once is the normal case, not an edge case --
+  /// a single FunctionDecl can be MSI-backed AND have an unresolved
+  /// exception spec at the same time), returns the subset of
+  /// FlaggedKinds that actually differ from the last recorded snapshot.
+  /// Real chain access, not a guess: builds a fresh footprint the same
+  /// way commit() does and compares it against chainFor/
+  /// memberSpecChainFor's mostRecent() via the same operator== every
+  /// StateAwareChain already uses for its own dedup. A bit with no
+  /// chain anywhere in this file (DeducedReturnType has none -- see its
+  /// own listener override's comment; TypeForDeclChanged/
+  /// EvaluatedValueCached likewise) is never returned as verified --
+  /// there is nothing to compare it against, so it cannot be confirmed,
+  /// full stop, not assumed either way.
+  // static uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds,
+  //                            PTUID ID);
+
+  void commitLevel1(PTUID ID, const Decl *D, MutationRecord &Rec,
+                    bool IsNew = false);
+
+  // respect the LIFO so only current inside map not commited can be commited
+  // not randon PTUID
+  // global map info shouldn't be commited before only added here. not note*
+  // time.
+  void commit(TranslationUnitDecl *MostRecentTU);
+
+  void restoreClassFamily(PTUID ID, const CXXRecordDecl *RD,
+                          MutationRecord &Rec);
+
+  void restoreFunctionFamily(PTUID ID, const FunctionDecl *FD,
+                             MutationRecord &Rec);
+
+  void restoreTemplateFamily(PTUID ID, const RedeclarableTemplateDecl *TD,
+                             MutationRecord &Rec);
+
+  void restoreVarFamily(PTUID ID, const VarDecl *VD, MutationRecord &Rec);
+
+  /// Every mutation this PTU recorded for an enum-shaped decl, or -- when CR
+  /// is non-null -- the baseline seed for one this PTU created.
+  ///
+  /// The smallest family: enums have no specialization category (there is no
+  /// such thing as an enum template), no deferred bodies, and no members with
+  /// independent mutable state -- EnumConstantDecls live and die with the
+  /// EnumDecl, so they are not separately tracked.
+  void restoreEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec);
+
+  void restoreLevel1(PTUID ID, const Decl *D, MutationRecord &Rec);
+
+  void rollback(TranslationUnitDecl *MostRecentTU);
+};
+
+class PTUMutationRecorder : public ASTMutationListener {
+private:
+  IncrementalStateTracker &Tracker;
+
+  template <typename TemplateT, typename SpecT>
+  void noteTemplateDeclMutation(const TemplateT *TD, const SpecT *Spec,
+                                DeclShape S) {}
+
+  void noteExceptionSpecMutation(const FunctionDecl *FD,
+                                 MutationType K = MutationType::ExceptionSpec) {
+  }
+
+  void noteDefinitionInstantiated(const Decl *D) {}
+
+public:
+  PTUMutationRecorder(IncrementalStateTracker &Tracker) : Tracker(Tracker) {}
+
+  /// A new TagDecl definition was completed.
+  void CompletedTagDefinition(const TagDecl *D) override {}
+
+  /// A new declaration with name has been added to a DeclContext.
+  void AddedVisibleDecl(const DeclContext *DC, const Decl *D) override {}
+
+  /// An implicit member was added after the definition was completed.
+  void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) override {
+  }
+
+  /// A template specialization (or partial one) was added to the
+  /// template declaration.
+  void AddedCXXTemplateSpecialization(
+      const ClassTemplateDecl *TD,
+      const ClassTemplateSpecializationDecl *D) override {}
+
+  /// A template specialization (or partial one) was added to the
+  /// template declaration.
+  void AddedCXXTemplateSpecialization(
+      const VarTemplateDecl *TD,
+      const VarTemplateSpecializationDecl *D) override {}
+
+  /// A template specialization (or partial one) was added to the
+  /// template declaration.
+  void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD,
+                                      const FunctionDecl *D) override {}
+
+  /// A function's exception specification has been evaluated or
+  /// instantiated.
+  void ResolvedExceptionSpec(const FunctionDecl *FD) override {}
+
+  /// A function's return type has been deduced.
+  void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override {
+  }
+
+  /// A virtual destructor's operator delete has been resolved.
+  void ResolvedOperatorDelete(const CXXDestructorDecl *DD,
+                              const FunctionDecl *Delete,
+                              Expr *ThisArg) override {}
+
+  /// A virtual destructor's operator global delete has been resolved.
+  void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
+                                  const FunctionDecl *GlobDelete) override {}
+
+  /// A virtual destructor's operator array delete has been resolved.
+  void ResolvedOperatorArrayDelete(const CXXDestructorDecl *DD,
+                                   const FunctionDecl *ArrayDelete) override {}
+
+  /// A virtual destructor's operator global array delete has been resolved.
+  void ResolvedOperatorGlobArrayDelete(
+      const CXXDestructorDecl *DD,
+      const FunctionDecl *GlobArrayDelete) override {}
+
+  /// An implicit member got a definition.
+  void CompletedImplicitDefinition(const FunctionDecl *D) override {
+    noteDefinitionInstantiated(D);
+  }
+
+  /// The instantiation of a templated function or variable was
+  /// requested. In particular, the point of instantiation and template
+  /// specialization kind of \p D may have changed.
+  void InstantiationRequested(const ValueDecl *D) override {}
+
+  /// A templated variable's definition was implicitly instantiated.
+  void VariableDefinitionInstantiated(const VarDecl *D) override {
+    noteDefinitionInstantiated(D);
+    // handle Memberinfo
+  }
+
+  /// A function template's definition was instantiated.
+  void FunctionDefinitionInstantiated(const FunctionDecl *D) override {
+    noteDefinitionInstantiated(D);
+    // handle Memberinfo
+  }
+
+  /// A default argument was instantiated.
+  void DefaultArgumentInstantiated(const ParmVarDecl *D) override {}
+
+  /// A default member initializer was instantiated.
+  void DefaultMemberInitializerInstantiated(const FieldDecl *D) override {}
+
+  /// A new objc category class was added for an interface.
+  void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD,
+                                    const ObjCInterfaceDecl *IFD) override {}
+
+  /// A declaration is marked used which was not previously marked used.
+  ///
+  /// \param D the declaration marked used
+  void DeclarationMarkedUsed(const Decl *D) override {}
+
+  /// A declaration is marked as OpenMP threadprivate which was not
+  /// previously marked as threadprivate.
+  ///
+  /// \param D the declaration marked OpenMP threadprivate.
+  void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override {}
+
+  /// A declaration is marked as OpenMP groupprivate which was not
+  /// previously marked as groupprivate.
+  ///
+  /// \param D the declaration marked OpenMP groupprivate.
+  void DeclarationMarkedOpenMPGroupPrivate(const Decl *D) override {}
+
+  /// A declaration is marked as OpenMP declaretarget which was not
+  /// previously marked as declaretarget.
+  ///
+  /// \param D the declaration marked OpenMP declaretarget.
+  /// \param Attr the added attribute.
+  void DeclarationMarkedOpenMPDeclareTarget(const Decl *D,
+                                            const Attr *Attr) override {}
+
+  /// A declaration is marked as a variable with OpenMP allocator.
+  ///
+  /// \param D the declaration marked as a variable with OpenMP allocator.
+  void DeclarationMarkedOpenMPAllocate(const Decl *D, const Attr *A) override {}
+
+  /// A declaration is marked as an OpenMP indirect call target.
+  ///
+  /// \param D the declaration marked as an indirect call target.
+  void DeclarationMarkedOpenMPIndirectCall(const Decl *D) override {}
+
+  /// A definition has been made visible by being redefined locally.
+  ///
+  /// \param D The definition that was previously not visible.
+  /// \param M The containing module in which the definition was made visible,
+  ///        if any.
+  void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override {}
+
+  /// An attribute was added to a RecordDecl
+  ///
+  /// \param Attr The attribute that was added to the Record
+  ///
+  /// \param Record The RecordDecl that got a new attribute
+  void AddedAttributeToRecord(const Attr *Attr,
+                              const RecordDecl *Record) override {}
+
+  /// An mangling number was added to a Decl
+  ///
+  /// \param D The decl that got a mangling number
+  ///
+  /// \param Number The mangling number that was added to the Decl
+  void AddedManglingNumber(const Decl *D, unsigned Number) override {}
+
+  /// An static local number was added to a Decl
+  ///
+  /// \param D The decl that got a static local number
+  ///
+  /// \param Number The static local number that was added to the Decl
+  void AddedStaticLocalNumbers(const Decl *D, unsigned Number) override {}
+
+  /// An anonymous namespace was added the translation unit decl
+  ///
+  /// \param TU The translation unit decl that got a new anonymous namespace
+  ///
+  /// \param AnonNamespace The anonymous namespace that was added
+  void AddedAnonymousNamespace(const TranslationUnitDecl *TU,
+                               NamespaceDecl *AnonNamespace) override {}
+
+  void AddedTagDeclType(const TagDecl *TD, const Type *T) override {}
+};
 } // end namespace clang
 #endif // LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index feb1b9eea5c7b..7e20dbf371b20 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -186,7 +186,7 @@ class SemaSystemZ;
 class SemaWasm;
 class SemaX86;
 class StandardConversionSequence;
-class SemaStateStash;
+class SemaStateRecovery;
 class TemplateArgument;
 class TemplateArgumentLoc;
 class TemplateInstantiationCallback;
@@ -1588,7 +1588,7 @@ class Sema final : public SemaBase {
   friend class ASTReader;
   friend class ASTDeclReader;
   friend class ASTWriter;
-  friend class SemaStateStash;
+  friend class SemaStateRecovery;
 
 private:
   std::optional<std::unique_ptr<DarwinSDKInfo>> CachedDarwinSDKInfo;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index e850e9a0b61d1..f0d760845d3b4 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5596,6 +5596,10 @@ CanQualType ASTContext::getCanonicalTagType(const TagDecl *TD) const {
       /*OwnsTag=*/false, /*IsInjected=*/false, /*CanonicalType=*/nullptr,
       /*WithFoldingSetNode=*/false);
   TD->TypeForDecl = CanonicalType;
+
+  if (ASTMutationListener *L = getASTMutationListener())
+    L->AddedTagDeclType(TD, TD->TypeForDecl);
+
   return CanQualType::CreateUnsafe(QualType(CanonicalType, 0));
 }
 
@@ -5622,10 +5626,11 @@ QualType ASTContext::getTagType(ElaboratedTypeKeyword Keyword,
                            /*OwnsTag=*/false, IsInjected, CanonicalType,
                            /*WithFoldingSetNode=*/false);
 
-    if (IncrementalErrorRecoveryMode)
-      PendingTypeForDeclMutations.push_back({const_cast<TagDecl *>(TD), TD->TypeForDecl});
-
     TD->TypeForDecl = T;
+
+    if (ASTMutationListener *L = getASTMutationListener())
+      L->AddedTagDeclType(TD, TD->TypeForDecl);
+
     return QualType(T, 0);
   }
 
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 4bc4b8ca2acdc..548643cea2cce 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -2211,9 +2211,6 @@ void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D, bool Internal) {
   }
 
   DeclNameEntries.addOrReplaceDecl(D);
-
-  if (getParentASTContext().isIncrementalErrorRecoveryMode())
-    getParentASTContext().PendingDCMutations.insert(this);
 }
 
 UsingDirectiveDecl *DeclContext::udir_iterator::operator*() const {
diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp
index 4d27b04248f93..bd9f4af9b7fd2 100644
--- a/clang/lib/Frontend/MultiplexConsumer.cpp
+++ b/clang/lib/Frontend/MultiplexConsumer.cpp
@@ -130,6 +130,7 @@ class MultiplexASTMutationListener : public ASTMutationListener {
   void AddedStaticLocalNumbers(const Decl *D, unsigned) override;
   void AddedAnonymousNamespace(const TranslationUnitDecl *,
                                NamespaceDecl *AnonNamespace) override;
+  void AddedTagDeclType(const TagDecl *TD, const Type *T) override;
 
 private:
   std::vector<ASTMutationListener*> Listeners;
@@ -278,6 +279,10 @@ void MultiplexASTMutationListener::AddedAnonymousNamespace(
   for (auto *L : Listeners)
     L->AddedAnonymousNamespace(TU, AnonNamespace);
 }
+void MultiplexASTMutationListener::AddedTagDeclType(const TagDecl *TD, const Type *T) {
+  for (auto *L : Listeners)
+    L->AddedTagDeclType(TD, T);
+}
 
 }  // end namespace clang
 
diff --git a/clang/lib/Interpreter/ASTContextStateStash.cpp b/clang/lib/Interpreter/ASTContextStateStash.cpp
index e25c67c46aa01..d9da3f7448da8 100644
--- a/clang/lib/Interpreter/ASTContextStateStash.cpp
+++ b/clang/lib/Interpreter/ASTContextStateStash.cpp
@@ -1,4 +1,4 @@
-//===--- ASTContextStateStash.cpp - ASTContext persistent state stash/restore
+//===--- ASTContextStateRecovery.cpp - ASTContext persistent state stash/restore
 //----------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -29,6 +29,29 @@
 namespace clang {
 
 /// Erases nodes from a FoldingSet based on a predicate.
+template <typename EntryType, typename PredT>
+static void eraseFoldingSetIf(llvm::UniquingSet<EntryType> &FS, PredT &&Pred) {
+  SmallVector<EntryType *, 16> ToRemove;
+  for (auto &N : FS)
+    if (Pred(N))
+      ToRemove.push_back(&N);
+
+  for (auto *N : ToRemove)
+    FS.erase(N);
+}
+
+template <typename EntryType, typename UniquingSetInfo, typename PredT>
+static void eraseFoldingSetIf(llvm::UniquingSet<EntryType, UniquingSetInfo> &FS,
+                              PredT &&Pred) {
+  SmallVector<EntryType *, 16> ToRemove;
+  for (auto &N : FS)
+    if (Pred(N))
+      ToRemove.push_back(&N);
+
+  for (auto *N : ToRemove)
+    FS.erase(N);
+}
+
 template <typename EntryType, typename PredT>
 static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
   SmallVector<EntryType *, 16> ToRemove;
@@ -37,7 +60,7 @@ static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
       ToRemove.push_back(&N);
 
   for (auto *N : ToRemove)
-    FS.RemoveNode(N);
+    FS.erase(N);
 }
 
 template <typename EntryType, typename PredT>
@@ -51,7 +74,7 @@ eraseFoldingSetIf(llvm::ContextualFoldingSet<EntryType, ASTContext &> &FS,
       ToRemove.push_back(&N);
 
   for (auto *N : ToRemove)
-    FS.RemoveNode(N);
+    FS.erase(N);
 }
 
 /// Erase from DenseMap based on predicate
@@ -79,7 +102,7 @@ static void eraseDenseSetIf(llvm::DenseSet<ValueT> &Set, PredT &&Pred) {
     Set.erase(Key);
 }
 
-void ASTContextStateStash::stash(StashCheckPoint &CP) {
+void ASTContextStateRecovery::stash(StashCheckPoint &CP) {
   CP.TypesSize = Ctx.Types.size();
   CP.ExtQualNodesSize = Ctx.ExtQualNodes.size();
   CP.ComplexTypesSize = Ctx.ComplexTypes.size();
@@ -188,7 +211,8 @@ void ASTContextStateStash::stash(StashCheckPoint &CP) {
   CP.GlobalArrayOperatorDeletesForVirtualDtorSize =
       Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size();
 
-  CP.RequireVectorDeletingDtorSize = Ctx.RequireVectorDeletingDtor.size();
+  CP.MaybeRequireVectorDeletingDtorSize =
+      Ctx.MaybeRequireVectorDeletingDtor.size();
 
   CP.MergedDeclsSize = Ctx.MergedDecls.size();
   CP.DeclAttrsSize = Ctx.DeclAttrs.size();
@@ -200,7 +224,7 @@ void ASTContextStateStash::stash(StashCheckPoint &CP) {
 
   CP.ScalableVecTyMapSize = Ctx.ScalableVecTyMap.size();
   CP.LambdaCastPathsSize = Ctx.LambdaCastPaths.size();
-  CP.DeclRawCommentsSize = Ctx.DeclRawComments.size();
+  CP.RawCommentsSize = Ctx.RawComments.size();
   CP.RedeclChainCommentsSize = Ctx.RedeclChainComments.size();
   CP.CommentlessRedeclChainsSize = Ctx.CommentlessRedeclChains.size();
   CP.ParsedCommentsSize = Ctx.ParsedComments.size();
@@ -236,11 +260,209 @@ void ASTContextStateStash::stash(StashCheckPoint &CP) {
 
   CP.ObjCClassDeclCP = Ctx.ObjCClassDecl;
 
-  Ctx.ObjCProtocolClassDecl = CP.ObjCProtocolClassDeclCP;
+  CP.ObjCProtocolClassDeclCP = Ctx.ObjCProtocolClassDecl;
+
+  /// DeclarationNameTable
+  CP.DeclarationNames.CXXConstructorNamesSize =
+      Ctx.DeclarationNames.CXXConstructorNames.size();
+  CP.DeclarationNames.CXXDestructorNamesSize =
+      Ctx.DeclarationNames.CXXDestructorNames.size();
+  CP.DeclarationNames.CXXConversionFunctionNamesSize =
+      Ctx.DeclarationNames.CXXConversionFunctionNames.size();
+  for (unsigned I = 0; I < NUM_OVERLOADED_OPERATORS; I++)
+    CP.DeclarationNames.CXXOperatorNamesCP[I] =
+        Ctx.DeclarationNames.CXXOperatorNames[I];
+  CP.DeclarationNames.CXXLiteralOperatorNamesSize =
+      Ctx.DeclarationNames.CXXLiteralOperatorNames.size();
+  CP.DeclarationNames.CXXDeductionGuideNamesSize =
+      Ctx.DeclarationNames.CXXDeductionGuideNames.size();
 }
 
-void ASTContextStateStash::restore(StashCheckPoint &CP,
-                                   llvm::SlabCheckPoint SlabCP) {
+class DeclContextRepairer {
+  ASTContext &Ctx;
+  llvm::SlabCheckPoint SlabCP;
+  // in case to avoid revisit.
+  llvm::SmallPtrSet<const DeclContext *, 8> Visited;
+
+  bool isAfterCP(const void *P) const {
+    return Ctx.getAllocator().isAfterCheckpoint(P, SlabCP);
+  }
+
+  static bool isExtensibleContainer(const Decl *D) {
+    return isa<NamespaceDecl>(D) || isa<TagDecl>(D);
+  }
+
+  static bool isRedeclarableOrOnlyDecl(Decl *D) {
+    return D->getPreviousDecl() != nullptr;
+  }
+
+  // Walk D's redecl chain looking for the newest decl that was from previous
+  // PTU. Returns nullptr if the entire chain was created this PTU.
+  template <typename DeclT> DeclT *findSurvivor(DeclT *D) {
+    for (DeclT *It = D->getMostRecentDecl(); It; It = It->getPreviousDecl()) {
+      if (!isAfterCP(It))
+        return It;
+      if (It == It->getFirstDecl())
+        break;
+    }
+    return nullptr;
+  }
+
+  template <typename decl_type>
+  void patchRedeclLink(Redeclarable<decl_type> *D, decl_type *Survivor) {
+    D->getFirstDecl()->RedeclLink.setLatest(Survivor);
+  }
+
+  void repairLexicalChain(DeclContext &DC) {
+    Decl *Prev = nullptr;
+    for (Decl *Cur = DC.FirstDecl; Cur; Cur = Cur->getNextDeclInContext()) {
+      if (isAfterCP(Cur)) {
+        if (Prev) {
+          DC.LastDecl = Prev;
+          Prev->NextInContextAndBits.setPointer(nullptr);
+        } else {
+          DC.FirstDecl = DC.LastDecl = nullptr; // nothing survives
+        }
+        return;
+      }
+      Prev = Cur;
+    }
+  }
+
+  NamedDecl *tryRepairRedeclChain(Decl *D) {
+    if (!isRedeclarableOrOnlyDecl(D))
+      return nullptr; // nothing to do
+
+    NamedDecl *Survivor = nullptr;
+
+    auto repairRedeclChain = [&](auto *RD) {
+      // using DeclT = std::remove_pointer_t<decltype(RD)>;
+
+      auto *SurvivorDecl = findSurvivor(RD);
+      if (SurvivorDecl)
+        patchRedeclLink(RD, SurvivorDecl);
+
+      Survivor = SurvivorDecl;
+    };
+
+    switch (D->getKind()) {
+    case Decl::Function:
+      repairRedeclChain(cast<FunctionDecl>(D));
+      break;
+
+    case Decl::Var:
+      repairRedeclChain(cast<VarDecl>(D));
+      break;
+
+    case Decl::Enum:
+      repairRedeclChain(cast<TagDecl>(D));
+      break;
+
+    case Decl::Record:
+    case Decl::CXXRecord:
+      repairRedeclChain(cast<TagDecl>(D));
+      break;
+
+    case Decl::ClassTemplate:
+      repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
+      break;
+
+    case Decl::FunctionTemplate:
+      repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
+      break;
+
+    case Decl::TypeAliasTemplate:
+      repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
+      break;
+
+    case Decl::VarTemplate:
+      repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
+      break;
+
+    case Decl::Namespace:
+      repairRedeclChain(cast<NamespaceDecl>(D));
+      break;
+
+    default:
+      break;
+    }
+
+    return Survivor;
+  }
+
+  void repairLookupEntry(StoredDeclsMap &Map, DeclarationName Key,
+                         StoredDeclsList &List, bool Recurse) {
+    // Snapshot first: remove()/addOrReplaceDecl() below invalidate the live
+    // view getLookupResult() returns.
+    SmallVector<NamedDecl *, 4> Snapshot(List.getLookupResult().begin(),
+                                         List.getLookupResult().end());
+
+    for (NamedDecl *D : Snapshot) {
+      if (Recurse && isExtensibleContainer(D))
+        repairDeclContextLookup(cast<DeclContext>(D), Recurse);
+
+      if (!isAfterCP(D))
+        continue; // predates this PTU -- untouched
+
+      NamedDecl *Survivor = tryRepairRedeclChain(D); // may be null
+
+      if (!Survivor) {
+        List.remove(D); // whole chain born this PTU
+        continue;
+      }
+      assert(Survivor != D && "survivor postdates the checkpoint");
+
+      // If the survivor is already visible under this name, D was an extra
+      // entry (an overload); otherwise D had REPLACED the survivor's slot
+      // via HandleRedeclaration and has to hand it back.
+      if (llvm::is_contained(Snapshot, Survivor))
+        List.remove(D);
+      else
+        List.addOrReplaceDecl(Survivor);
+    }
+
+    if (List.isNull())
+      Map.erase(Key);
+  }
+
+public:
+  DeclContextRepairer(ASTContext &Ctx, llvm::SlabCheckPoint CP)
+      : Ctx(Ctx), SlabCP(CP) {}
+
+  template <typename decl_type>
+  void repairRedeclchain(Redeclarable<decl_type> *D) {
+    decl_type *SurviourDecl = findSurvivor(D);
+    if (SurviourDecl)
+      patchRedeclLink(D, SurviourDecl);
+  }
+
+  // Public entry point -- recursivly repair affected DCs.
+  void repairDeclContextLookup(DeclContext *DC, bool NoRecursive = false) {
+    DeclContext *Primary = DC->getPrimaryContext();
+    if (!Visited.insert(Primary).second)
+      return;
+
+    bool CompletelyNew = isAfterCP(cast<Decl>(Primary)->getCanonicalDecl());
+    StoredDeclsMap *Map = Primary->getLookupPtr();
+
+    if (CompletelyNew) {
+      // Primary's very first declaration happened this PTU -- the entire
+      // entity is being discarded as a unit, so there's nothing to
+      // individually repair.
+      if (Map)
+        Map->clear();
+      return;
+    }
+
+    if (Map)
+      for (auto &Entry : *Map)
+        repairLookupEntry(*Map, Entry.first, Entry.second, NoRecursive);
+    repairLexicalChain(*Primary);
+  }
+};
+
+void ASTContextStateRecovery::restore(StashCheckPoint &CP,
+                                      llvm::SlabCheckPoint SlabCP) {
   if (CP.AutoDeductTy != Ctx.AutoDeductTy) {
     llvm::dbgs() << "Ctx.AutoDeductTy != CP.AutoDeductTy\n";
     Ctx.AutoDeductTy = CP.AutoDeductTy;
@@ -678,8 +900,8 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
     // mutable llvm::DenseMap<llvm::FoldingSetNodeID, AutoType *> AutoTypes;
     eraseDenseMapIf(
         Ctx.AutoTypes,
-        [&](llvm::detail::DenseMapPair<llvm::FoldingSetNodeID, AutoType *> &KV)
-            -> bool {
+        [&](llvm::detail::DenseMapPair<llvm::FoldingSetNodeIDRef, AutoType *>
+                &KV) -> bool {
           return Ctx.getAllocator().isAfterCheckpoint(
               static_cast<void *>(KV.getSecond()), SlabCP);
         });
@@ -1110,17 +1332,18 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
            Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size());
   }
 
-  if (CP.RequireVectorDeletingDtorSize !=
-      Ctx.RequireVectorDeletingDtor.size()) {
-    llvm::dbgs() << "if (CP.RequireVectorDeletingDtorSize != "
-                    "Ctx.RequireVectorDeletingDtor.size()\n";
-    eraseDenseSetIf(
-        Ctx.RequireVectorDeletingDtor, [&](const CXXRecordDecl *RD) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-              static_cast<void *>(const_cast<CXXRecordDecl *>(RD)), SlabCP);
-        });
-    assert(CP.RequireVectorDeletingDtorSize ==
-           Ctx.RequireVectorDeletingDtor.size());
+  if (CP.MaybeRequireVectorDeletingDtorSize !=
+      Ctx.MaybeRequireVectorDeletingDtor.size()) {
+    llvm::dbgs() << "if (CP.MaybeRequireVectorDeletingDtorSize != "
+                    "Ctx.MaybeRequireVectorDeletingDtor.size()\n";
+    eraseDenseSetIf(Ctx.MaybeRequireVectorDeletingDtor,
+                    [&](const CXXRecordDecl *RD) -> bool {
+                      return Ctx.getAllocator().isAfterCheckpoint(
+                          static_cast<void *>(const_cast<CXXRecordDecl *>(RD)),
+                          SlabCP);
+                    });
+    assert(CP.MaybeRequireVectorDeletingDtorSize ==
+           Ctx.MaybeRequireVectorDeletingDtor.size());
   }
 
   if (CP.MergedDeclsSize != Ctx.MergedDecls.size()) {
@@ -1176,9 +1399,9 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
     assert(CP.LambdaCastPathsSize == Ctx.LambdaCastPaths.size());
   }
 
-  if (CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()) {
-    llvm::dbgs() << "CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()\n";
-    assert(CP.DeclRawCommentsSize == Ctx.DeclRawComments.size());
+  if (CP.RawCommentsSize != Ctx.RawComments.size()) {
+    llvm::dbgs() << "CP.RawCommentsSize != Ctx.RawComments.size()\n";
+    assert(CP.RawCommentsSize == Ctx.RawComments.size());
   }
 
   if (CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()) {
@@ -1224,6 +1447,23 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   if (CP.TemplateOrInstantiationSize != Ctx.TemplateOrInstantiation.size()) {
     llvm::dbgs() << "CP.TemplateOrInstantiationSize != "
                     "Ctx.TemplateOrInstantiation.size()\n";
+    //                     llvm::DenseMap<const VarDecl *,
+    //                     TemplateOrSpecializationInfo>
+    // TemplateOrInstantiation;
+    eraseDenseMapIf(
+        Ctx.TemplateOrInstantiation,
+        [&](llvm::detail::DenseMapPair<
+            const VarDecl *, ASTContext::TemplateOrSpecializationInfo> &KV)
+            -> bool {
+          ASTContext::TemplateOrSpecializationInfo &T = KV.getSecond();
+          bool IsAfterCP = false;
+          if (auto *M = T.dyn_cast<MemberSpecializationInfo *>())
+            IsAfterCP = Ctx.getAllocator().isAfterCheckpoint(M, SlabCP);
+          if (auto *V = T.dyn_cast<VarTemplateDecl *>())
+            IsAfterCP = Ctx.getAllocator().isAfterCheckpoint(V, SlabCP);
+          return IsAfterCP ||
+                 Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
+        });
     assert(CP.TemplateOrInstantiationSize ==
            Ctx.TemplateOrInstantiation.size());
   }
@@ -1339,7 +1579,6 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
     assert(CP.TraversalScopeSize == Ctx.TraversalScope.size());
   }
 
-
   /// Obj-C
   if (CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()) {
     llvm::dbgs() << "CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()\n";
@@ -1383,55 +1622,120 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
     assert(CP.ObjCProtocolClassDeclCP == Ctx.ObjCProtocolClassDecl);
   }
 
-  for (auto &[Decl, OldTy] : Ctx.PendingTypeForDeclMutations)
-    Decl->TypeForDecl = OldTy;
-
-  Ctx.PendingTypeForDeclMutations.clear();
-  TranslationUnitDecl *MostRecentTU = Ctx.getTranslationUnitDecl();
-  for (const auto *DC : Ctx.PendingDCMutations) {
-    if (MostRecentTU->getPrimaryContext() == DC)
-      continue;
-    if (StoredDeclsMap *Map = const_cast<DeclContext *>(DC)
-                                  ->getPrimaryContext()
-                                  ->getLookupPtr()) {
-      for (auto &&[Key, List] : *Map) {
-        DeclContextLookupResult R = List.getLookupResult();
-        std::vector<NamedDecl *> NamedDeclsToRemove;
-        // bool RemoveAll = true;
-        for (NamedDecl *D : R) {
-          // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU (" <<
-          // (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
-          // llvm::outs() << "DeclContext : " << DC << "\n";
-          // D->dump();
-          // if (D->getTranslationUnitDecl() == MostRecentTU)
-          if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D),
-                                                   SlabCP))
-            NamedDeclsToRemove.push_back(D);
-          // else
-          //   RemoveAll = false;
-        }
-        // if (LLVM_LIKELY(RemoveAll)) {
-        //   Map->erase(Key);
-        // } else {
-        for (NamedDecl *D : NamedDeclsToRemove)
-          List.remove(D);
-        // }
-      }
-    }
-
-    // Decl *Prev = DC->FirstDecl;
-    // Decl *Cur = DC->FirstDecl;
-    // while (Cur) {
-    //   if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
-    //                                            SlabCP)) {
-    //     DC->LastDecl = Prev;
-    //     DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
-    //     break;
-    //   }
-    //   Prev = Cur;
-    //   Cur = Cur->getNextDeclInContext();
-    // }
-  }
+  if (CP.DeclarationNames.CXXConstructorNamesSize !=
+      Ctx.DeclarationNames.CXXConstructorNames.size()) {
+    llvm::dbgs() << "CP.DeclarationNames.CXXConstructorNamesSize != "
+                    "Ctx.DeclarationNames.CXXConstructorNames.size()";
+    eraseFoldingSetIf(Ctx.DeclarationNames.CXXConstructorNames,
+                      [&](detail::CXXSpecialNameExtra &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(&Node,
+                                                                    SlabCP);
+                      });
+    assert(CP.DeclarationNames.CXXConstructorNamesSize ==
+           Ctx.DeclarationNames.CXXConstructorNames.size());
+  }
+  if (CP.DeclarationNames.CXXDestructorNamesSize !=
+      Ctx.DeclarationNames.CXXDestructorNames.size()) {
+    llvm::dbgs() << "CP.DeclarationNames.CXXDestructorNamesSize != "
+                    "Ctx.DeclarationNames.CXXDestructorNames.size()";
+    eraseFoldingSetIf(Ctx.DeclarationNames.CXXDestructorNames,
+                      [&](detail::CXXSpecialNameExtra &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(&Node,
+                                                                    SlabCP);
+                      });
+    assert(CP.DeclarationNames.CXXDestructorNamesSize ==
+           Ctx.DeclarationNames.CXXDestructorNames.size());
+  }
+  if (CP.DeclarationNames.CXXConversionFunctionNamesSize !=
+      Ctx.DeclarationNames.CXXConversionFunctionNames.size()) {
+    llvm::dbgs() << "CP.DeclarationNames.CXXConversionFunctionNamesSize != "
+                    "Ctx.DeclarationNames.CXXConversionFunctionNames.size()";
+    eraseFoldingSetIf(Ctx.DeclarationNames.CXXConversionFunctionNames,
+                      [&](detail::CXXSpecialNameExtra &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(&Node,
+                                                                    SlabCP);
+                      });
+    assert(CP.DeclarationNames.CXXConversionFunctionNamesSize ==
+           Ctx.DeclarationNames.CXXConversionFunctionNames.size());
+  }
+  if (CP.DeclarationNames.CXXLiteralOperatorNamesSize !=
+      Ctx.DeclarationNames.CXXLiteralOperatorNames.size()) {
+    llvm::dbgs() << "CP.DeclarationNames.CXXLiteralOperatorNamesSize != "
+                    "Ctx.DeclarationNames.CXXLiteralOperatorNames.size()";
+    eraseFoldingSetIf(Ctx.DeclarationNames.CXXLiteralOperatorNames,
+                      [&](detail::CXXLiteralOperatorIdName &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(&Node,
+                                                                    SlabCP);
+                      });
+    assert(CP.DeclarationNames.CXXLiteralOperatorNamesSize ==
+           Ctx.DeclarationNames.CXXLiteralOperatorNames.size());
+  }
+  if (CP.DeclarationNames.CXXDeductionGuideNamesSize !=
+      Ctx.DeclarationNames.CXXDeductionGuideNames.size()) {
+    llvm::dbgs() << "CP.DeclarationNames.CXXDeductionGuideNamesSize != "
+                    "Ctx.DeclarationNames.CXXDeductionGuideNames.size()";
+    eraseFoldingSetIf(Ctx.DeclarationNames.CXXDeductionGuideNames,
+                      [&](detail::CXXDeductionGuideNameExtra &Node) -> bool {
+                        return Ctx.getAllocator().isAfterCheckpoint(&Node,
+                                                                    SlabCP);
+                      });
+    assert(CP.DeclarationNames.CXXDeductionGuideNamesSize ==
+           Ctx.DeclarationNames.CXXDeductionGuideNames.size());
+  }
+  for (unsigned I = 0; I < NUM_OVERLOADED_OPERATORS; I++)
+    Ctx.DeclarationNames.CXXOperatorNames[I] =
+        CP.DeclarationNames.CXXOperatorNamesCP[I];
+
+  // for (auto &[Decl, OldTy] : Ctx.PendingTypeForDeclMutations)
+  //   Decl->TypeForDecl = OldTy;
+
+  // Ctx.PendingTypeForDeclMutations.clear();
+  // TranslationUnitDecl *MostRecentTU = Ctx.getTranslationUnitDecl();
+  // for (const auto *DC : Ctx.PendingDCMutations) {
+  //   // if (MostRecentTU->getPrimaryContext() == DC)
+  //   //   continue;
+  //   if (StoredDeclsMap *Map = const_cast<DeclContext *>(DC)
+  //                                 ->getPrimaryContext()
+  //                                 ->getLookupPtr()) {
+  //     for (auto &&[Key, List] : *Map) {
+  //       DeclContextLookupResult R = List.getLookupResult();
+  //       std::vector<NamedDecl *> NamedDeclsToRemove;
+  //       // bool RemoveAll = true;
+  //       for (NamedDecl *D : R) {
+  //         // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU ("
+  //         <<
+  //         // (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
+  //         // llvm::outs() << "DeclContext : " << DC << "\n";
+  //         // D->dump();
+  //         // if (D->getTranslationUnitDecl() == MostRecentTU)
+  //         if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D),
+  //                                                  SlabCP))
+  //           NamedDeclsToRemove.push_back(D);
+  //         // else
+  //         // RemoveAll = false;
+  //       }
+  //       // if (LLVM_LIKELY(RemoveAll)) {
+  //       //   Map->erase(Key);
+  //       // } else {
+  //       for (NamedDecl *D : NamedDeclsToRemove)
+  //         List.remove(D);
+  //       // }
+  //     }
+  //   }
+
+  // Decl *Prev = DC->FirstDecl;
+  // Decl *Cur = DC->FirstDecl;
+  // while (Cur) {
+  //   if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
+  //                                            SlabCP)) {
+  //     DC->LastDecl = Prev;
+  //     DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
+  //     break;
+  //   }
+  //   Prev = Cur;
+  //   Cur = Cur->getNextDeclInContext();
+  // }
+  // }
 
   // if (FirstDecl) {
   //   LastDecl->NextInContextAndBits.setPointer(D);
@@ -1469,8 +1773,6 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   // /// end of the import declaration.
   // llvm::PointerIntPair<ImportDecl *, 1, bool> NextLocalImportAndComplete;
 
-  Ctx.PendingDCMutations.clear();
-
   // llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM = Ctx.LastSDM;
 
   // StoredDeclsMap *Map = LastSDM.getPointer();
@@ -1502,8 +1804,5 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
   Ctx.Types.resize(CP.TypesSize);
 }
 
-void ASTContextStateStash::commit() {
-  Ctx.PendingTypeForDeclMutations.clear();
-  Ctx.PendingDCMutations.clear();
-}
+void ASTContextStateRecovery::commit() {}
 } // end namespace clang
\ No newline at end of file
diff --git a/clang/lib/Interpreter/IncrementalAction.cpp b/clang/lib/Interpreter/IncrementalAction.cpp
index 751cc7b58a034..2c1ad404a3e7d 100644
--- a/clang/lib/Interpreter/IncrementalAction.cpp
+++ b/clang/lib/Interpreter/IncrementalAction.cpp
@@ -54,7 +54,7 @@ IncrementalAction::IncrementalAction(CompilerInstance &Instance,
         }
         return Act;
       }()),
-      Interp(I), CI(Instance), LLVMCtx(LLVMCtx), Consumer(std::move(Consumer)) {}
+      Interp(I), CI(Instance), Consumer(std::move(Consumer)) {}
 
 std::unique_ptr<ASTConsumer>
 IncrementalAction::CreateASTConsumer(CompilerInstance & /*CI*/,
@@ -97,10 +97,8 @@ llvm::Module *IncrementalAction::getCachedCodeGenModule() const {
   return CachedInCodeGenModule.get();
 }
 
-std::unique_ptr<llvm::Module> IncrementalAction::GenModule(bool WasFailure) {
+std::unique_ptr<llvm::Module> IncrementalAction::GenModule() {
   static unsigned ID = 0;
-  if (WasFailure)
-    --ID;
   if (CodeGenerator *CG = getCodeGen()) {
     // Clang's CodeGen is designed to work with a single llvm::Module. In many
     // cases for convenience various CodeGen parts have a reference to the
@@ -117,10 +115,8 @@ std::unique_ptr<llvm::Module> IncrementalAction::GenModule(bool WasFailure) {
              CachedInCodeGenModule->alias_empty() &&
              CachedInCodeGenModule->ifunc_empty())) &&
            "CodeGen wrote to a readonly module");
-    std::unique_ptr<llvm::Module> M = nullptr;
-    if (!WasFailure)
-      M = CG->ReleaseModule();
-    CG->StartModule("incr_module_" + std::to_string(ID++), M ? M->getContext() : LLVMCtx);
+    std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
+    CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
     return M;
   }
   return nullptr;
diff --git a/clang/lib/Interpreter/IncrementalAction.h b/clang/lib/Interpreter/IncrementalAction.h
index 13461dee7500f..c3e4f108ea365 100644
--- a/clang/lib/Interpreter/IncrementalAction.h
+++ b/clang/lib/Interpreter/IncrementalAction.h
@@ -36,7 +36,7 @@ class IncrementalAction : public WrapperFrontendAction {
   bool IsTerminating = false;
   Interpreter &Interp;
   [[maybe_unused]] CompilerInstance &CI;
-  llvm::LLVMContext &LLVMCtx;
+  // llvm::LLVMContext &LLVMCtx;
   std::unique_ptr<ASTConsumer> Consumer;
 
   /// When CodeGen is created the first llvm::Module gets cached in many places
@@ -75,7 +75,7 @@ class IncrementalAction : public WrapperFrontendAction {
   CodeGenerator *getCodeGen() const;
 
   /// Generate an LLVM module for the most recent parsed input.
-  std::unique_ptr<llvm::Module> GenModule(bool WasFailure = false);
+  std::unique_ptr<llvm::Module> GenModule();
 };
 
 class InProcessPrintingASTConsumer final : public MultiplexConsumer {
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index 6f913867ad159..ed8919d8af59e 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -17,8 +17,12 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclContextInternals.h"
+<<<<<<< HEAD
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclVisitor.h"
+=======
+#include "clang/CodeGen/ModuleBuilder.h"
+>>>>>>> 6d90b8675095 (Add Initial impl ErrorRecovery.h)
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"
 #include "clang/Parse/Parser.h"
@@ -88,9 +92,8 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
 
   DiagnosticsEngine &Diags = S.getDiagnostics();
   if (Diags.hasErrorOccurred()) {
-    Consumer->HandleTranslationUnit(C);
     CleanUpPTU(C.getTranslationUnitDecl());
-
+    // Consumer->HandleTranslationUnit(C);
     Diags.Reset(/*soft=*/true);
     Diags.getClient()->clear();
     return llvm::make_error<llvm::StringError>("Parsing failed.",
@@ -330,8 +333,8 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
 
   // Lookup alone is not enough: the redeclaration chain still reaches these.
   withdrawMostRecentTU(MostRecentTU);
-  RepairRedeclChain(MostRecentTU, MostRecentTU);
-  S.getASTContext().setTranslationUnitDecl(MostRecentTU->getPreviousDecl());
+  // RepairRedeclChain(MostRecentTU, MostRecentTU);
+  // S.getASTContext().setTranslationUnitDecl(MostRecentTU->getPreviousDecl());
 }
 
 PartialTranslationUnit &
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 63961133b907d..bf9d20653727a 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -593,8 +593,8 @@ Interpreter::Parse(llvm::StringRef Code) {
 
   private:
     ASTContext &Ctx;
-    ASTContextStateStash ASTCtxState;
-    SemaStateStash SemaState;
+    ASTContextStateRecovery ASTCtxState;
+    SemaStateRecovery SemaState;
     llvm::SlabCheckPoint CheckPoint;
     bool Committed = false;
     StashCheckPoint CtxCheckPoint;
@@ -624,11 +624,11 @@ Interpreter::Parse(llvm::StringRef Code) {
   getCompilerInstance()->getDiagnostics().setSeverity(
       clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation());
 
-  PTUSlabRollback Rollback(CI->getSema());
+  // PTUSlabRollback Rollback(CI->getSema());
 
   llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
   if (!TuOrErr) {
-    Act->GenModule(true);
+    // Act->GenModule();
     return TuOrErr.takeError();
   }
 
@@ -640,7 +640,7 @@ Interpreter::Parse(llvm::StringRef Code) {
           frontend::EmitLLVM)
     LastPTU.TheModule->print(llvm::outs(), /*AAW=*/nullptr);
 
-  Rollback.commit(LastPTU);
+  // Rollback.commit(LastPTU);
   return LastPTU;
 }
 
diff --git a/clang/lib/Interpreter/SemaStateStash.cpp b/clang/lib/Interpreter/SemaStateStash.cpp
index e72d0b7580bcb..1c24b634b9443 100644
--- a/clang/lib/Interpreter/SemaStateStash.cpp
+++ b/clang/lib/Interpreter/SemaStateStash.cpp
@@ -1,4 +1,4 @@
-//===--- SemaStateStash.cpp - Sema persistent state stash/restore
+//===--- SemaStateRecovery.cpp - Sema persistent state stash/restore
 //----------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -29,8 +29,7 @@
 namespace clang {
 
 template <typename EntryType, typename PredT>
-static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred)
-{
+static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
   SmallVector<EntryType *, 16> ToRemove;
   for (auto &N : FS)
     if (Pred(N))
@@ -116,7 +115,7 @@ static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, SmallSize> &Set,
 // Pragma / value snapshot (uses Sema friend access for nested types)
 //===----------------------------------------------------------------------===//
 
-// struct SemaStateStash::PragmaSnapshot {
+// struct SemaStateRecovery::PragmaSnapshot {
 //   Sema::PragmaClangSection PragmaClangBSSSection;
 //   Sema::PragmaClangSection PragmaClangDataSection;
 //   Sema::PragmaClangSection PragmaClangRodataSection;
@@ -140,7 +139,7 @@ static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, SmallSize> &Set,
 //   FileNullabilityMap NullabilityMap;
 // };
 
-void SemaStateStash::stash(SemaStashCheckPoint &CP) {
+void SemaStateRecovery::stash(SemaStashCheckPoint &CP) {
   CP.SemaBumpSlabCP = S.BumpAlloc.checkPoint();
   // CP.CachedFunctionScopeSize = S.CachedFunctionScope.size();
   CP.FunctionScopesSize = S.FunctionScopes.size();
@@ -230,7 +229,7 @@ void SemaStateStash::stash(SemaStashCheckPoint &CP) {
       S.CodeSynthesisContextLookupModules.size();
   CP.LookupModulesCacheSize = S.LookupModulesCache.size();
   CP.VisibleNamespaceCacheSize = S.VisibleNamespaceCache.size();
-  CP.TemplateInstCallbacksSize = S.TemplateInstCallbacks.size();
+  // CP.TemplateInstCallbacksSize = S.TemplateInstCallbacks.size();
   CP.PendingInstantiationsSize = S.PendingInstantiations.size();
   CP.LateParsedInstantiationsSize = S.LateParsedInstantiations.size();
   CP.SavedVTableUsesSize = S.SavedVTableUses.size();
@@ -239,6 +238,8 @@ void SemaStateStash::stash(SemaStashCheckPoint &CP) {
       S.PendingLocalImplicitInstantiations.size();
   CP.UnsubstitutedConstraintSatisfactionCacheSize =
       S.UnsubstitutedConstraintSatisfactionCache.size();
+  if (S.CurrentCachedTemplateArgs)
+    CP.CurrentCachedTemplateArgsSize = S.CurrentCachedTemplateArgs->size();
   CP.SubsumptionCacheSize = S.SubsumptionCache.size();
   CP.NormalizationCacheSize = S.NormalizationCache.size();
   CP.SatisfactionCacheSize = S.SatisfactionCache.size();
@@ -248,8 +249,8 @@ void SemaStateStash::stash(SemaStashCheckPoint &CP) {
   // CP.AllEffectsToVerifySize = S.AllEffectsToVerify.size();
 }
 
-void SemaStateStash::restore(SemaStashCheckPoint &CP,
-                             llvm::SlabCheckPoint SlabCP) {
+void SemaStateRecovery::restore(SemaStashCheckPoint &CP,
+                                llvm::SlabCheckPoint SlabCP) {
 
   ASTContext &Ctx = S.getASTContext();
   if (CP.FunctionScopesSize != S.FunctionScopes.size()) {
@@ -438,7 +439,8 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
         *S.PureVirtualClassDiagSet.get(), [&](const CXXRecordDecl *RD) -> bool {
           return Ctx.getAllocator().isAfterCheckpoint(RD, SlabCP);
         });
-    // assert(CP.PureVirtualClassDiagSetSize == S.PureVirtualClassDiagSet->size());
+    // assert(CP.PureVirtualClassDiagSetSize ==
+    // S.PureVirtualClassDiagSet->size());
   }
 
   if (CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.end()) {
@@ -464,7 +466,7 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
 
   if (CP.UndefinedButUsedSize != S.UndefinedButUsed.size()) {
     llvm::dbgs() << "CP.UndefinedButUsedSize != S.UndefinedButUsed.size()\n";
-      // llvm::MapVector<NamedDecl *, SourceLocation> UndefinedButUsed;
+    // llvm::MapVector<NamedDecl *, SourceLocation> UndefinedButUsed;
     while (CP.UndefinedButUsedSize != S.UndefinedButUsed.size())
       S.UndefinedButUsed.pop_back();
     assert(CP.UndefinedButUsedSize == S.UndefinedButUsed.size());
@@ -580,11 +582,13 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
                     "S.TypoCorrectionFailures.size()\n";
     // eraseDenseMapIf(
     //     S.TypoCorrectionFailures,
-    //     [&](llvm::detail::DenseMapPair<IdentifierInfo *, Sema::SrcLocSet> &KV)
+    //     [&](llvm::detail::DenseMapPair<IdentifierInfo *, Sema::SrcLocSet>
+    //     &KV)
     //         -> bool {
-    //           llvm::outs() << "SlabCheckPoint Cur : " << (void *)SlabCP.CurPtr << "\n";
-    //           llvm::outs() << "SlabCheckPoint END : " << (void *)SlabCP.End << "\n";
-    //           llvm::outs() << "Addr: " << static_cast<void *>(KV.getFirst()) << "\n";
+    //           llvm::outs() << "SlabCheckPoint Cur : " << (void
+    //           *)SlabCP.CurPtr << "\n"; llvm::outs() << "SlabCheckPoint END :
+    //           " << (void *)SlabCP.End << "\n"; llvm::outs() << "Addr: " <<
+    //           static_cast<void *>(KV.getFirst()) << "\n";
     //       return Ctx.getAllocator().isAfterCheckpoint(
     //           static_cast<void *>(KV.getFirst()), SlabCP);
     //     });
@@ -737,13 +741,13 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
     assert(CP.VisibleNamespaceCacheSize == S.VisibleNamespaceCache.size());
   }
 
-  if (CP.TemplateInstCallbacksSize != S.TemplateInstCallbacks.size()) {
-    llvm::dbgs() << "CP.TemplateInstCallbacksSize != "
-                    "S.TemplateInstCallbacks.size()\n";
-    //    std::vector<std::unique_ptr<TemplateInstantiationCallback>>
-    //   TemplateInstCallbacks;
-    assert(CP.TemplateInstCallbacksSize == S.TemplateInstCallbacks.size());
-  }
+  // if (CP.TemplateInstCallbacksSize != S.TemplateInstCallbacks.size()) {
+  //   llvm::dbgs() << "CP.TemplateInstCallbacksSize != "
+  //                   "S.TemplateInstCallbacks.size()\n";
+  //   //    std::vector<std::unique_ptr<TemplateInstantiationCallback>>
+  //   //   TemplateInstCallbacks;
+  //   assert(CP.TemplateInstCallbacksSize == S.TemplateInstCallbacks.size());
+  // }
 
   if (CP.PendingInstantiationsSize != S.PendingInstantiations.size()) {
     llvm::dbgs() << "CP.PendingInstantiationsSize != "
@@ -787,6 +791,22 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
            S.PendingLocalImplicitInstantiations.size());
   }
 
+  if (S.CurrentCachedTemplateArgs && (CP.CurrentCachedTemplateArgsSize !=
+                                      S.CurrentCachedTemplateArgs->size())) {
+    llvm::dbgs() << "CP.CurrentCachedTemplateArgsSize != "
+                    "S.CurrentCachedTemplateArgs->size()\n ";
+    // eraseDenseMapIf(
+    //     S.UnsubstitutedConstraintSatisfactionCache,
+    //     [&](UnsubstitutedConstraintSatisfactionCacheResult
+    //     &R) -> bool {
+    //       return Ctx.isAfterCheckpoint(static_cast<void
+    //       *>(R.SubstExpr.get()),
+    //                                    SlabCP)
+    //     });
+    assert(CP.CurrentCachedTemplateArgsSize ==
+           S.CurrentCachedTemplateArgs->size());
+  }
+
   if (CP.UnsubstitutedConstraintSatisfactionCacheSize !=
       S.UnsubstitutedConstraintSatisfactionCache.size()) {
     llvm::dbgs() << "CP.UnsubstitutedConstraintSatisfactionCacheSize != "
@@ -859,12 +879,15 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
   if (CP.SpecialMemberCacheSize != S.SpecialMemberCache.size()) {
     llvm::dbgs() << "CP.SpecialMemberCacheSize != "
                     "S.SpecialMemberCache.size()\n";
-    
-    eraseFoldingSetIf(
+
+    eraseDenseMapIf(
         S.SpecialMemberCache,
-        [&](Sema::SpecialMemberOverloadResultEntry &Node) -> bool {
-          return S.BumpAlloc.isAfterCheckpoint(static_cast<void *>(&Node),
-                                               CP.SemaBumpSlabCP);
+        [&](llvm::detail::DenseMapPair<Sema::SpecialMemberCacheKey,
+                                       Sema::SpecialMemberOverloadResult> &KV)
+            -> bool {
+          return S.BumpAlloc.isAfterCheckpoint(
+              static_cast<void *>(KV.getSecond().getMethod()),
+              CP.SemaBumpSlabCP);
         });
     assert(CP.SpecialMemberCacheSize == S.SpecialMemberCache.size());
   }
@@ -876,7 +899,8 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
     assert(isa<NamedDecl>(TmpD) && "Decl isn't NamedDecl?");
     NamedDecl *D = cast<NamedDecl>(TmpD);
 
-    if (!D->getDeclName()) continue;
+    if (!D->getDeclName())
+      continue;
 
     if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D), SlabCP)) {
       if (D->getDeclName().getFETokenInfo())
diff --git a/clang/test/Interpreter/ptu-rewind-stress.cpp b/clang/test/Interpreter/ptu-rewind-stress.cpp
index f38b1c5fb9731..48f3f0835af31 100644
--- a/clang/test/Interpreter/ptu-rewind-stress.cpp
+++ b/clang/test/Interpreter/ptu-rewind-stress.cpp
@@ -4,137 +4,21 @@
 // incremental input, the same way fail.cpp / code-undo.cpp do):
 //   RUN: cat %s | clang-repl | FileCheck %s
 //
-// Usage interactively: paste blocks (or the whole file) directly at the
-// `clang-repl>` prompt. Each block is a single line on purpose -- clang-repl
-// treats one physical line as one Interpreter::Parse() call, and several of
-// these tests rely on packing a well-formed declaration and an ill-formed
-// one onto the *same* line so both go through Sema together before the
-// chunk is discarded as a whole.
-//
-// -----------------------------------------------------------------------
-// Observed results (assertions-enabled build, this checkout, macOS/arm64)
-// -----------------------------------------------------------------------
-// Running this file as-is through `./bin/clang-repl < ptu-rewind-stress.cpp`
-// does NOT make it to the %quit at the bottom:
-//  * Test 1 (dangling_name) printed "dangling_name = 7" instead of 99, and
-//    the JIT reported
-//      error: In incr_module_89, duplicate definition of symbol '_dangling_name'
-//    i.e. the rolled-back chunk's *codegen* (which runs incrementally per
-//    top-level decl, before the end-of-chunk error check) was never undone
-//    either, so the second, successful declaration silently reused the
-//    first (supposedly-discarded) global.
-//  * Test 3 (Boom<int> template instantiation) reliably aborts the process:
-//      Assertion failed: (CP.UndefinedButUsedSize == S.UndefinedButUsed.size()),
-//      function restore, file SemaStateStash.cpp, line 457.
-//    i.e. real, confirmed, unconditional test-3-and-later block; a debug/
-//    assertions build never reaches tests 4-7 in one session because of
-//    this abort.
-//  * Running tests 4-7 in isolation (skipping 1-3), Test 4 (sizeof on a
-//    pre-existing type) produced
-//      JIT session error: Symbols not found: [ _sz_known2 ]
-//      error: Failed to materialize symbols: ...
-//    followed by the process spinning at 100% CPU instead of returning to
-//    the prompt for the remaining input (tests 5-7 never ran in that
-//    session either).
-//  * Running Test 1 completely on its own (no preceding chunks) did NOT
-//    reproduce the wrong-value/duplicate-symbol failure -- it printed the
-//    correct "dangling_name = 99". This is expected for a poisoned/reused-
-//    memory bug: whether a stale pointer's target has been overwritten by
-//    something that "looks wrong" depends on what the allocator handed out
-//    to *other* code in between, so the manifestation is sensitive to the
-//    exact preceding session history, not just to the isolated snippet.
-//    That is precisely why this file chains many small scenarios in one
-//    session rather than shipping them as independent one-liners.
-//
-// -----------------------------------------------------------------------
-// Background
-// -----------------------------------------------------------------------
-// Interpreter::Parse (clang/lib/Interpreter/Interpreter.cpp) wraps every
-// incremental input in a PTUSlabRollback guard:
-//   1. Takes CheckPoint = Ctx.getAllocator().checkPoint()
-//   2. Stashes ASTContext/Sema side-table sizes (ASTContextStateStash,
-//      SemaStateStash)
-//   3. Runs IncrementalParser::Parse()
-//   4. On success: commits (keeps everything)
-//   5. On failure: SemaState.restore(), ASTCtxState.restore(), then
-//      Ctx.getAllocator().restoreToCheckPoint(CheckPoint)
-//
-// restoreToCheckPoint (llvm/include/llvm/Support/Allocator.h) does not just
-// move a pointer back -- it actively memset()s the reclaimed range to 0xCD
-// ("poisonMemory") outside of ASan builds. So *any* pointer left behind in a
-// side-table that the two StateStash::restore() functions fail to clean up
-// is not merely suspect, it is guaranteed to point at either poisoned bytes
-// or, once new allocations reuse that space, at a completely unrelated
-// object (type confusion).
-//
-// Reading ASTContextStateStash.cpp and SemaStateStash.cpp shows the
-// restore() paths fall into three buckets:
-//   (a) real cleanup: erase from the container based on
-//       isAfterCheckpoint(ptr, SlabCP)  [handles most Type folding sets]
-//   (b) resize()/pop-back style cleanup on trailing-append vectors
-//       [FunctionScopes, LateParsedInstantiations, SavedVTableUses, ...]
-//   (c) `assert(oldSize == newSize)` with NO actual erase -- compiled away
-//       entirely under NDEBUG, so the container is left holding dangling
-//       pointers with no diagnostic at all in a release build
-//       [StringLiteralCache, KeyFunctions-adjacent maps when the early
-//       return below fires, MergedDecls, TemplateInstCallbacks,
-//       PendingInstantiations, LateParsedTemplateMap, VTableUses/
-//       VTablesUsed, ...]
-// Additionally, ASTContextStateStash::restore() opens with:
-//       if (CP.TypesSize == Ctx.Types.size()) return;
-// which skips *all* of the above (including the folding sets that do have
-// real cleanup logic) whenever the number of interned Type nodes happens to
-// be unchanged -- even though many other caches (record layouts, key
-// functions, ...) can grow without interning a new Type.
-//
-// Every test below is built to land in one of these gaps. None of the
-// "poison" code paths depend on undefined behavior sanitizers to observe --
-// under a debug (assertions-enabled) build several of them should abort on
-// the assert() itself; under a release build the same inputs should
-// eventually crash, print garbage, or misbehave once the poisoned/reused
-// memory is dereferenced.
-// -----------------------------------------------------------------------
 
 extern "C" int printf(const char *, ...);
 
 // =======================================================================
 // Test 1: Sema::IdResolver keeps a dangling entry after a failed parse.
-//
-// SemaStateStash::restore() (clang/lib/Interpreter/SemaStateStash.cpp)
-// walks S.getCurScope()->decls() and calls Scope::RemoveDecl() for every
-// decl allocated after the checkpoint -- but the matching
-// `S.IdResolver.RemoveDecl(D)` call right above it is commented out. Name
-// lookup goes through IdResolver, not just Scope, so `dangling_name`'s
-// VarDecl stays "found" by lookup even though its storage is about to be
-// poisoned by the allocator rewind.
-//
-// `int dangling_name = 7;` fully binds (Scope + IdResolver + DeclContext
-// lookup map) before `intentional_error_type` is even parsed, because
-// IncrementalParser::ParseOrWrapTopLevelDecl only checks
-// Diags.hasErrorOccurred() *after* parsing every top-level decl in the
-// chunk. So both statements are discarded together, but only Scope forgets
-// about `dangling_name`.
 // =======================================================================
 int dangling_name = 7; intentional_error_type garbage_after_dangling_name;
 
-// Redeclare the same identifier in a fresh, successful chunk. If the stale
-// IdResolver entry survived, Sema's redeclaration-merging logic
-// (Sema::MergeVarDecl* et al.) may try to compare this new VarDecl against
-// the dangling one -- reading 0xCD-poisoned memory, or memory since reused
-// by an unrelated allocation.
 int dangling_name = 99;
 auto t1 = printf("dangling_name = %d\n", dangling_name);
 // CHECK: dangling_name = 99
 
 // =======================================================================
 // Test 2: Amplify test 1 by repeating the fail/redeclare cycle several
-// times on the same identifier. Because every failed chunk takes its
-// checkpoint at (almost) the same allocator offset, each failed attempt's
-// storage for `stress_var` gets reallocated over the *same* address range
-// poisoned by the previous attempt. Any stale IdResolver node left behind
-// by an earlier iteration therefore ends up aliasing whatever the *next*
-// iteration (or the final, successful declaration) allocates there --
-// classic type-confusion setup, and a good candidate to run under ASan.
+// times on the same identifier.
 // =======================================================================
 int stress_var = 0; intentional_error_type e0;
 int stress_var = 1; intentional_error_type e1;
@@ -147,18 +31,6 @@ auto t2 = printf("stress_var = %d\n", stress_var);
 
 // =======================================================================
 // Test 3: Sema::PendingInstantiations does not survive a failed parse.
-//
-// ParseOrWrapTopLevelDecl() returns *before* calling
-// LocalInstantiations.perform()/GlobalInstantiations.perform() whenever
-// Diags.hasErrorOccurred() -- see the early `return` right after
-// CleanUpPTU() in IncrementalParser.cpp. So any instantiation work queued
-// while parsing `Boom<int> boom_instance; boom_instance.trigger();` is never
-// drained on this path. SemaStateStash::restore() only compares
-// S.PendingInstantiations.size() with an assert (it's a std::deque, never
-// resized/erased), so a stale PendingImplicitInstantiation entry pointing
-// at the (about-to-be-poisoned) `Boom<int>::trigger` specialization can
-// leak into whatever the *next* successful chunk's own eager-instantiation
-// pass processes.
 // =======================================================================
 template <typename T> struct Boom { void trigger() { T v; (void)v; } };
 Boom<int> boom_instance; boom_instance.trigger(); intentional_error_type boom_garbage;
@@ -174,19 +46,6 @@ auto t3 = printf("after_boom_marker = %d\n", after_boom_marker);
 // =======================================================================
 // Test 4: ASTContextStateStash's Types.size() early-return guard skips
 // cleanup of caches that don't depend on interning a new Type.
-//
-//   void ASTContextStateStash::restore(...) {
-//     if (CP.TypesSize == Ctx.Types.size())
-//       return;
-//     ... (all the real per-folding-set cleanup lives below this line) ...
-//
-// `AlreadyKnown` already exists (its RecordType was interned when it was
-// first declared, in the prior committed chunk). Computing sizeof() on it
-// only populates Ctx.ASTRecordLayouts (and, since it's a POD aggregate here,
-// nothing else) -- it does not intern a new Type, so Ctx.Types.size() is
-// unchanged across this failing chunk and the whole restore() body,
-// including ASTRecordLayouts's own (otherwise correct) erase-by-checkpoint
-// logic, is skipped.
 // =======================================================================
 struct AlreadyKnown { int a; int b; };
 unsigned long sz_known = sizeof(AlreadyKnown); intentional_error_type sizeof_garbage;
@@ -201,10 +60,6 @@ auto t4 = printf("sizeof(AlreadyKnown) = %lu\n", sz_known2);
 // Test 5: Virtual dispatch bookkeeping (Sema::VTableUses/VTablesUsed,
 // ASTContext::KeyFunctions) is assert-only / guard-gated, same as above,
 // but exercised through polymorphic classes and `new` instead of sizeof.
-// RecordLayoutBuilder.cpp populates Ctx.KeyFunctions[RD] while laying out
-// `Derived` for the `new` expression below; Sema::MarkVTableUsed populates
-// VTableUses/VTablesUsed. Both are torn down on a *committed* chunk but
-// only assert-compared on a rolled-back one.
 // =======================================================================
 struct Base { virtual int val() { return 1; } virtual ~Base() {} };
 struct Derived : Base { int val() override { return 2; } };
@@ -219,35 +74,19 @@ auto t5 = printf("val = %d\n", bp2->val());
 // CHECK: val = 2
 
 // =======================================================================
-// Test 6: Ctx.StringLiteralCache has no erase logic at all -- not gated by
-// the Types.size() guard (defining a brand-new function interns a new
-// FunctionProtoType, so the early return above does *not* fire here), just
-// a bare `assert(CP.StringLiteralCacheSize == Ctx.StringLiteralCache.size())`
-// with nothing to actually undo the insertion made by
-// ASTContext::getPredefinedStringLiteralFromCache when Sema processes
-// __PRETTY_FUNCTION__. In an assertions-enabled build this is the test most
-// likely to abort immediately and deterministically, independent of memory
-// poisoning, purely because the size check itself fails.
+// Test 6: StringLiteralCache has no erase logic, so processing PRETTY_FUNCTION
+// leaves an extra entry and can make the cache-size assertion fail deterministically.
 // =======================================================================
 void uses_predefined_expr() { const char* pf = __PRETTY_FUNCTION__; (void)pf; } intentional_error_type predefined_garbage;
 
-// A second, differently-named function that also uses __PRETTY_FUNCTION__,
-// forcing another StringLiteralCache lookup/insert keyed differently from
-// the discarded one above.
 void uses_predefined_expr_again() { const char* pf = __PRETTY_FUNCTION__; (void)pf; }
 int pf_ran = 1;
 auto t6 = printf("pf_ran = %d\n", pf_ran);
 // CHECK: pf_ran = 1
 
 // =======================================================================
-// Test 7: Cross-declaration merging state (Ctx.MergedDecls,
-// Ctx.InstantiatedFromUsingShadowDecl) survives a failed using-declaration.
-// A `using` declaration creates a UsingShadowDecl tied back to the
-// original NS::helper via a side map that SemaStateStash/
-// ASTContextStateStash only assert-compare. If the failed attempt's
-// UsingShadowDecl (and its bookkeeping entry) is left dangling, a
-// subsequent successful `using NS::helper;` shares the same DeclarationName
-// and could resolve through, or conflict with, the stale shadow chain.
+// Test 7: Cross-declaration merging state can survive a failed using-declaration,
+// leaving stale UsingShadowDecl bookkeeping that may affect a later successful using.
 // =======================================================================
 namespace NS { int helper() { return 10; } }
 using NS::helper; intentional_error_type merge_garbage;
diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index b611e015e84d7..75f654dac058e 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -620,6 +620,11 @@ template <class T, class VectorT = SmallVector<T *, 8>> class FoldingSetVector {
     Vector.push_back(N);
   }
 
+  void pop_back() {
+    Set.erase(Vector.back());
+    Vector.pop_back();
+  }
+
   /// Insert the specified node into the folding set, knowing that
   /// it is not already in the folding set.
   void insert(T *N) {
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index 3709972ea99d8..0aceaa999e2a6 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -45,8 +45,7 @@ LLVM_ABI void printBumpPtrAllocatorStats(unsigned NumSlabs, size_t TotalMemory);
 struct SlabCheckPoint {
   unsigned ActiveSlabIdx;
   char *CurPtr;
-  char *End;
-  size_t BytesAllocated;
+  uintptr_t EndSentinel;
 };
 
 /// Allocate memory in an ever growing pool, as if by bump-pointer.
@@ -223,7 +222,7 @@ class BumpPtrAllocatorImpl
       void *NewSlab = Slabs[ActiveSlabIdx];
       size_t AllocatedSlabSize = computeSlabSize(ActiveSlabIdx);
       CurPtr = (char *)(NewSlab);
-      End = ((char *)NewSlab) + AllocatedSlabSize;
+      EndSentinel = uintptr_t(NewSlab) + AllocatedSlabSize + 1;
     } else
       // Otherwise, start a new slab and try again.
       StartNewSlab();
@@ -259,7 +258,7 @@ class BumpPtrAllocatorImpl
   size_t GetNumSlabs() const { return Slabs.size() + CustomSizedSlabs.size(); }
 
   SlabCheckPoint checkPoint() const {
-    return {ActiveSlabIdx, CurPtr, End, BytesAllocated};
+    return {ActiveSlabIdx, CurPtr, EndSentinel};
   }
 
   static void poisonMemory(void *Ptr, size_t Size) {
@@ -298,16 +297,19 @@ class BumpPtrAllocatorImpl
   void restoreToCheckPoint(SlabCheckPoint CP) {
     assert(CP.ActiveSlabIdx >= 0 && CP.ActiveSlabIdx < Slabs.size());
     assert(CP.CurPtr >= (const char *)Slabs[CP.ActiveSlabIdx] &&
-           CP.End == ((const char *)Slabs[CP.ActiveSlabIdx] +
-                      computeSlabSize(CP.ActiveSlabIdx)));
+           CP.EndSentinel == uintptr_t(Slabs[CP.ActiveSlabIdx]) +
+                      computeSlabSize(CP.ActiveSlabIdx) + 1);
     ActiveSlabIdx = CP.ActiveSlabIdx;
     CurPtr = CP.CurPtr;
-    End = CP.End;
-    BytesAllocated = CP.BytesAllocated;
-    llvm::outs() << "Poisoned range = [" << (void *)CurPtr << ", " << (void *)End << ")\n";
-    llvm::outs() << "Poisoned End Size = [" << (void *)CurPtr << ", " << (void *)(CurPtr + (size_t)(End - CurPtr)) << ")\n";
+    EndSentinel = CP.EndSentinel;
+
+    uintptr_t EndRange =
+        uintptr_t(Slabs[CP.ActiveSlabIdx]) + computeSlabSize(CP.ActiveSlabIdx);
+
+    llvm::outs() << "Poisoned range = [" << (void *)CurPtr << ", " << (void *)(EndSentinel - 1) << "]\n";
+    llvm::outs() << "Poisoned End Size = [" << (void *)CurPtr << ", " << (void *)(CurPtr + ((EndSentinel - 1) - (uintptr_t)CurPtr)) << "]\n";
     llvm::outs().flush();
-    poisonMemory((void *)CurPtr, (size_t)(End - CurPtr));
+    poisonMemory((void *)CurPtr, (size_t)(EndRange - uintptr_t(CurPtr)));
     for (unsigned I = ActiveSlabIdx + 1; I < Slabs.size(); ++I)
       // Should we deallocate any extra slabs?
       poisonMemory(Slabs[I], computeSlabSize(I));

>From 415d3654dbc45ae44e253d5a9d65bd63fcda358f Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 16 Sep 2026 12:22:58 +0530
Subject: [PATCH 04/13] Refactor code: update PR scope to decl state recovery

---
 clang/include/clang/AST/ASTContext.h          |    6 -
 clang/include/clang/AST/ASTMutationListener.h |    6 -
 clang/include/clang/AST/Decl.h                |    3 -
 clang/include/clang/AST/DeclBase.h            |   11 -
 clang/include/clang/AST/DeclCXX.h             |    5 +-
 .../include/clang/AST/DeclContextInternals.h  |    3 -
 clang/include/clang/AST/DeclTemplate.h        |    5 -
 clang/include/clang/AST/DeclarationName.h     |    2 -
 clang/include/clang/AST/RecordLayout.h        |    2 -
 clang/include/clang/AST/Redeclarable.h        |    4 -
 .../include/clang/Interpreter/ErrorRecovery.h |  547 ++---
 clang/include/clang/Sema/Sema.h               |    2 -
 clang/lib/AST/ASTContext.cpp                  |    9 -
 clang/lib/Frontend/MultiplexConsumer.cpp      |    5 -
 .../lib/Interpreter/ASTContextStateStash.cpp  | 1808 -----------------
 clang/lib/Interpreter/CMakeLists.txt          |    2 -
 clang/lib/Interpreter/IncrementalAction.h     |    1 -
 clang/lib/Interpreter/IncrementalParser.cpp   |    2 +-
 clang/lib/Interpreter/Interpreter.cpp         |   24 +-
 clang/lib/Interpreter/SemaStateStash.cpp      |  918 ---------
 20 files changed, 203 insertions(+), 3162 deletions(-)
 delete mode 100644 clang/lib/Interpreter/ASTContextStateStash.cpp
 delete mode 100644 clang/lib/Interpreter/SemaStateStash.cpp

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index aaefe14fa828d..e3d09c8257d83 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -85,7 +85,6 @@ template <> struct DenseMapInfo<ScalableVecTyKey> {
 namespace clang {
 
 class APValue;
-class ASTContextStateRecovery;
 class ASTMutationListener;
 class ASTRecordLayout;
 class AtomicExpr;
@@ -614,7 +613,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
   template <class> friend class serialization::AbstractTypeReader;
   friend class CXXRecordDecl;
   friend class IncrementalParser;
-  friend class ASTContextStateRecovery;
 
   /// A mapping to contain the template or declaration that
   /// a variable declaration describes or was instantiated from,
@@ -1362,10 +1360,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
     TUDecl = NewTUDecl;
   }
 
-  void setTranslationUnitDecl(TranslationUnitDecl *NewTUDecl) {
-    TUDecl = NewTUDecl;
-  }
-
   ExternCContextDecl *getExternCContextDecl() const;
 
 #define BuiltinTemplate(BTName) BuiltinTemplateDecl *get##BTName##Decl() const;
diff --git a/clang/include/clang/AST/ASTMutationListener.h b/clang/include/clang/AST/ASTMutationListener.h
index 14cb3b1d14612..7540b7f90ee86 100644
--- a/clang/include/clang/AST/ASTMutationListener.h
+++ b/clang/include/clang/AST/ASTMutationListener.h
@@ -195,12 +195,6 @@ class ASTMutationListener {
   virtual void AddedAnonymousNamespace(const TranslationUnitDecl *TU,
                                        NamespaceDecl *AnonNamespace) {}
 
-  /// A TagDecl's cached TypeForDecl was materialized for the first time.
-  ///
-  /// \param TD The declaration whose type cache was populated.
-  /// \param T The Type now cached. Never null.
-  virtual void AddedTagDeclType(const TagDecl *TD, const Type *T) {}
-
   // NOTE: If new methods are added they should also be added to
   // MultiplexASTMutationListener.
 };
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 05a102d584a57..c64dc8c02d40e 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -57,7 +57,6 @@
 namespace clang {
 
 class ASTContext;
-class ASTStateReader;
 struct ASTTemplateArgumentListInfo;
 class CompoundStmt;
 class DependentFunctionTemplateSpecializationInfo;
@@ -3649,7 +3648,6 @@ class IndirectFieldDecl : public ValueDecl,
 class TypeDecl : public NamedDecl {
   friend class ASTContext;
   friend class ASTReader;
-  friend class ASTStateReader;
 
   /// This indicates the Type object that represents
   /// this TypeDecl.  It is a cache maintained by
@@ -3916,7 +3914,6 @@ class TagDecl : public TypeDecl,
 
 public:
   friend class ASTDeclReader;
-  friend class ASTStateReader;
   friend class ASTDeclWriter;
 
   using redecl_range = redeclarable_base::redecl_range;
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 9dd1f60011ce0..8ea2533825dd9 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -43,13 +43,10 @@
 namespace clang {
 
 class ASTContext;
-class ASTContextStateRecovery;
-class ASTDeclDetacher;
 class ASTMutationListener;
 class Attr;
 class BlockDecl;
 class DeclContext;
-class DeclContextRepairer;
 class ExternalSourceSymbolAttr;
 class FunctionDecl;
 class FunctionType;
@@ -261,9 +258,6 @@ class alignas(8) Decl {
 
 private:
   friend class DeclContext;
-  // friend class ASTContextStateRecovery;
-  friend class ASTDeclDetacher;
-  friend class DeclContextRepairer;
 
   struct MultipleDC {
     DeclContext *SemanticDC;
@@ -1351,7 +1345,6 @@ namespace clang {
 /// A list storing NamedDecls in the lookup tables.
 class DeclListNode {
   friend class ASTContext; // allocate, deallocate nodes.
-  friend class ASTContextStateRecovery;
   friend class StoredDeclsList;
 public:
   using Decls = llvm::PointerUnion<NamedDecl*, DeclListNode*>;
@@ -1471,10 +1464,6 @@ enum class LinkageSpecLanguageIDs;
 ///   BlockDecl
 ///   CapturedDecl
 class DeclContext {
-  /// For restoring decl to its previous state (clang-repl error-recovery).
-  friend class ASTDeclDetacher;
-
-  friend class DeclContextRepairer;
   /// For makeDeclVisibleInContextImpl
   friend class ASTDeclReader;
   /// For checking the new bits in the Serialization part.
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 539cf28a76031..74122971a1492 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -54,8 +54,7 @@
 namespace clang {
 
 class ASTContext;
-class ASTDeclDetacher;
-class ASTStateReader;
+class DeclStateReverter;
 class ClassTemplateDecl;
 class ConstructorUsingShadowDecl;
 class CXXBasePath;
@@ -265,9 +264,9 @@ class CXXRecordDecl : public RecordDecl {
   friend class ASTNodeImporter;
   friend class ASTReader;
   friend class ASTRecordWriter;
-  friend class ASTStateReader;
   friend class ASTWriter;
   friend class DeclContext;
+  friend class DeclStateReverter;
   friend class LambdaExpr;
   friend class ODRDiagsEmitter;
 
diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h
index e7d9c2d742dc8..87820b14d6188 100644
--- a/clang/include/clang/AST/DeclContextInternals.h
+++ b/clang/include/clang/AST/DeclContextInternals.h
@@ -301,13 +301,10 @@ class StoredDeclsList {
   }
 };
 
-class ASTContextStateRecovery;
-
 class StoredDeclsMap
     : public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> {
   friend class ASTContext; // walks the chain deleting these
   friend class DeclContext;
-  friend class ASTContextStateRecovery;
 
   llvm::PointerIntPair<StoredDeclsMap*, 1> Previous;
 public:
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index a4beffb99465b..fd1c7df6e7813 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -46,8 +46,6 @@
 
 namespace clang {
 
-class ASTDeclDetacher;
-class ASTStateReader;
 enum BuiltinTemplateKind : int;
 class ClassTemplateDecl;
 class ClassTemplatePartialSpecializationDecl;
@@ -826,9 +824,7 @@ class RedeclarableTemplateDecl : public TemplateDecl,
 public:
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
-  friend class ASTDeclDetacher;
   friend class ASTReader;
-  friend class ASTStateReader;
 
   template <class decl_type> friend class RedeclarableTemplate;
 
@@ -2317,7 +2313,6 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
 
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
-  friend class ASTStateReader;
   friend class TemplateDeclInstantiator;
 
   /// Load any lazily-loaded specializations from the external source.
diff --git a/clang/include/clang/AST/DeclarationName.h b/clang/include/clang/AST/DeclarationName.h
index dd490c688de18..5121b561288c0 100644
--- a/clang/include/clang/AST/DeclarationName.h
+++ b/clang/include/clang/AST/DeclarationName.h
@@ -32,7 +32,6 @@
 namespace clang {
 
 class ASTContext;
-class ASTContextStateRecovery;
 template <typename> class CanQual;
 class DeclarationName;
 class DeclarationNameTable;
@@ -592,7 +591,6 @@ inline bool operator>=(DeclarationName LHS, DeclarationName RHS) {
 /// uniqued versions of each of the C++ special names, which can be
 /// retrieved using its member functions (e.g., getCXXConstructorName).
 class DeclarationNameTable {
-  friend class ASTContextStateRecovery;
   /// Used to allocate elements in the FoldingSets below.
   const ASTContext &Ctx;
 
diff --git a/clang/include/clang/AST/RecordLayout.h b/clang/include/clang/AST/RecordLayout.h
index a7964b69feb03..9af17df229b37 100644
--- a/clang/include/clang/AST/RecordLayout.h
+++ b/clang/include/clang/AST/RecordLayout.h
@@ -26,7 +26,6 @@
 namespace clang {
 
 class ASTContext;
-class ASTContextStateRecovery;
 class CXXRecordDecl;
 
 /// ASTRecordLayout -
@@ -61,7 +60,6 @@ class ASTRecordLayout {
 
 private:
   friend class ASTContext;
-  friend class ASTContextStateRecovery;
 
   /// Size - Size of record in characters.
   CharUnits Size;
diff --git a/clang/include/clang/AST/Redeclarable.h b/clang/include/clang/AST/Redeclarable.h
index 150c310530c03..fe2648aa58301 100644
--- a/clang/include/clang/AST/Redeclarable.h
+++ b/clang/include/clang/AST/Redeclarable.h
@@ -25,9 +25,7 @@
 namespace clang {
 
 class ASTContext;
-class ASTDeclDetacher;
 class Decl;
-class DeclContextRepairer;
 
 // Some notes on redeclarables:
 //
@@ -191,8 +189,6 @@ class Redeclarable {
   friend class ASTDeclUnmerger;
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
-  friend class ASTDeclDetacher;
-  friend class DeclContextRepairer;
   friend class IncrementalParser;
 
   Redeclarable(const ASTContext &Ctx)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 0c47e9bc2296b..2ef75b2492fa2 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -24,309 +24,6 @@
 namespace clang {
 class ASTContext;
 
-struct SemaStashCheckPoint {
-  /// Sema::*
-  llvm::SlabCheckPoint SemaBumpSlabCP;
-  size_t CachedFunctionScopeSize = 0;
-  size_t FunctionScopesSize = 0;
-  size_t Ident_superSize = 0;
-  size_t PragmaClangBSSSectionSize = 0;
-  size_t PragmaClangDataSectionSize = 0;
-  size_t PragmaClangRodataSectionSize = 0;
-  size_t PragmaClangRelroSectionSize = 0;
-  size_t PragmaClangTextSectionSize = 0;
-  size_t VtorDispStackSize = 0;
-  size_t AlignPackStackSize = 0;
-  size_t AlignPackIncludeStackSize = 0;
-  size_t DataSegStackSize = 0;
-  size_t BSSSegStackSize = 0;
-  size_t ConstSegStackSize = 0;
-  size_t CodeSegStackSize = 0;
-  size_t StrictGuardStackCheckStackSize = 0;
-  size_t FpPragmaStackSize = 0;
-  size_t FunctionToSectionMapSize = 0;
-  size_t PragmaAttributeStackSize = 0;
-  size_t MSFunctionNoBuiltinsSize = 0;
-  size_t PendingExportedNamesSize = 0;
-  size_t TypoCorrectedFunctionDefinitionsSize = 0;
-  size_t FlagBitsCacheSize = 0;
-  size_t AssignEnumCacheSize = 0;
-  size_t WeakUndeclaredIdentifiersSize = 0;
-  size_t ExtnameUndeclaredIdentifiersSize = 0;
-  size_t UnusedLocalTypedefNameCandidatesSize = 0;
-  Sema::UnusedFileScopedDeclsType::iterator UnusedFileScopedDeclsSize;
-  Sema::TentativeDefinitionsType::iterator TentativeDefinitionsSize;
-  size_t ExternalDeclarationsSize = 0;
-  size_t ParsingInitForAutoVarsSize = 0;
-  size_t DeclsToCheckForDeferredDiagsSize = 0;
-  size_t ShadowingDeclsSize = 0;
-  size_t WeakTopLevelDeclSize = 0;
-  Sema::ExtVectorDeclsType::iterator ExtVectorDeclsSize;
-  size_t VTableUsesSize = 0;
-  size_t VTablesUsedSize = 0;
-  size_t DelayedDllExportClassesSize = 0;
-  size_t DelayedDllExportMemberFunctionsSize = 0;
-  size_t InventedParameterInfosSize = 0;
-  size_t FieldCollectorSize = 0;
-  size_t UnusedPrivateFieldsSize = 0;
-  size_t PureVirtualClassDiagSetSize = 0;
-  Sema::DelegatingCtorDeclsType::iterator DelegatingCtorDeclsSize;
-  size_t StdNamespaceSize = 0;
-  size_t UnparsedDefaultArgLocsSize = 0;
-  size_t UndefinedButUsedSize = 0;
-  size_t SpecialMembersBeingDeclaredSize = 0;
-  size_t DelayedOverridingExceptionSpecChecksSize = 0;
-  size_t DelayedEquivalentExceptionSpecChecksSize = 0;
-  size_t MaybeODRUseExprsSize = 0;
-  size_t RefsMinusAssignmentsSize = 0;
-  size_t ExprCleanupObjectsSize = 0;
-  size_t ExprEvalContextsSize = 0;
-  size_t FailedImmediateInvocationsSize = 0;
-  size_t ImplicitlyRetainedSelfLocsSize = 0;
-  size_t DeleteExprsSize = 0;
-  size_t CurrentParameterCopyTypesSize = 0;
-  size_t AggregateDeductionCandidatesSize = 0;
-  size_t TypoCorrectionFailuresSize = 0;
-  size_t SpecialMemberCacheSize = 0;
-  size_t ModuleScopesSize = 0;
-  size_t DeferredExportedNamespacesSize = 0;
-  size_t PendingInlineFuncDeclsSize = 0;
-  size_t CurrentSEHFinallySize = 0;
-  size_t CurrentDeferSize = 0;
-  size_t LateParsedTemplateMapSize = 0;
-  size_t SuppressedDiagnosticsSize = 0;
-  size_t CurrentInstantiationScopeSize = 0;
-  size_t UnparsedDefaultArgInstantiationsSize = 0;
-  size_t CodeSynthesisContextsSize = 0;
-  size_t InstantiatingSpecializationsSize = 0;
-  size_t InstantiatedNonDependentTypesSize = 0;
-  size_t CodeSynthesisContextLookupModulesSize = 0;
-  size_t LookupModulesCacheSize = 0;
-  size_t VisibleNamespaceCacheSize = 0;
-  // size_t TemplateInstCallbacksSize = 0;
-  size_t PendingInstantiationsSize = 0;
-  size_t LateParsedInstantiationsSize = 0;
-  size_t SavedVTableUsesSize = 0;
-  size_t SavedPendingInstantiationsSize = 0;
-  size_t PendingLocalImplicitInstantiationsSize = 0;
-  size_t CurrentCachedTemplateArgsSize = 0;
-  size_t UnsubstitutedConstraintSatisfactionCacheSize = 0;
-  size_t SubsumptionCacheSize = 0;
-  size_t NormalizationCacheSize = 0;
-  size_t SatisfactionCacheSize = 0;
-  size_t SatisfactionStackSize = 0;
-  //   size_t NullabilityMapSize = 0;
-  size_t DeclsWithEffectsToVerifySize = 0;
-  //   size_t AllEffectsToVerifySize = 0;
-};
-
-/// Stashes and restores persistent Sema state around an incremental parse.
-///
-/// Usage:
-///   SemaStashCheckPoint CP;
-///   SemaStateRecovery Stash(S);
-///   Stash.stash(CP);
-///   // ... parse ...
-///   if (failed)
-///     Stash.restore(CP, ASTSlabCP);
-class SemaStateRecovery {
-  Sema &S;
-
-  /// Holds full copies of PragmaStack, PragmaClangSection, FileNullabilityMap,
-  /// etc.
-  //   struct PragmaSnapshot;
-  //   std::unique_ptr<PragmaSnapshot> Pragmas;
-
-public:
-  explicit SemaStateRecovery(Sema &S) : S(S) {}
-  //   ~SemaStateRecovery();
-
-  void stash(SemaStashCheckPoint &CP);
-  void restore(SemaStashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
-};
-
-struct StashCheckPoint {
-  // Types vector
-  size_t TypesSize = 0;
-
-  // FoldingSets — store count of nodes
-  size_t ExtQualNodesSize = 0;
-  size_t ComplexTypesSize = 0;
-  size_t PointerTypesSize = 0;
-  size_t AdjustedTypesSize = 0;
-  size_t BlockPointerTypesSize = 0;
-  size_t LValueReferenceTypesSize = 0;
-  size_t RValueReferenceTypesSize = 0;
-  size_t MemberPointerTypesSize = 0;
-
-  size_t ConstantArrayTypesSize = 0;
-  size_t IncompleteArrayTypesSize = 0;
-  size_t VariableArrayTypesSize = 0;
-
-  size_t DependentSizedArrayTypesSize = 0;
-  size_t DependentSizedExtVectorTypesSize = 0;
-  size_t DependentAddressSpaceTypesSize = 0;
-  size_t VectorTypesSize = 0;
-  size_t DependentVectorTypesSize = 0;
-  size_t MatrixTypesSize = 0;
-  size_t DependentSizedMatrixTypesSize = 0;
-  size_t FunctionNoProtoTypesSize = 0;
-  size_t FunctionProtoTypesSize = 0;
-  size_t DependentTypeOfExprTypesSize = 0;
-  size_t DependentDecltypeTypesSize = 0;
-
-  size_t DependentPackIndexingTypesSize = 0;
-
-  size_t TemplateTypeParmTypesSize = 0;
-  size_t ObjCTypeParamTypesSize = 0;
-  size_t SubstTemplateTypeParmTypesSize = 0;
-  size_t SubstTemplateTypeParmPackTypesSize = 0;
-  size_t SubstBuiltinTemplatePackTypesSize = 0;
-
-  size_t TemplateSpecializationTypesSize = 0;
-  size_t ParenTypesSize = 0;
-  size_t TagTypesSize = 0;
-  size_t UnresolvedUsingTypesSize = 0;
-  size_t UsingTypesSize = 0;
-  size_t TypedefTypesSize = 0;
-  size_t DependentNameTypesSize = 0;
-  size_t PackExpansionTypesSize = 0;
-  size_t UnaryTransformTypesSize = 0;
-
-  size_t AutoTypesSize = 0;
-  size_t DeducedTemplateSpecializationTypesSize = 0;
-  size_t AtomicTypesSize = 0;
-  size_t AttributedTypesSize = 0;
-  size_t PipeTypesSize = 0;
-  size_t BitIntTypesSize = 0;
-  size_t DependentBitIntTypesSize = 0;
-  size_t BTFTagAttributedTypesSize = 0;
-  size_t HLSLAttributedResourceTypesSize = 0;
-  size_t HLSLInlineSpirvTypesSize = 0;
-
-  size_t CountAttributedTypesSize = 0;
-
-  size_t QualifiedTemplateNamesSize = 0;
-  size_t DependentTemplateNamesSize = 0;
-  size_t SubstTemplateTemplateParmsSize = 0;
-  size_t SubstTemplateTemplateParmPacksSize = 0;
-  size_t DeducedTemplatesSize = 0;
-
-  size_t ArrayParameterTypesSize = 0;
-
-  size_t PredefinedSugarTypesSize = 0;
-
-  size_t NamespaceAndPrefixStoragesSize = 0;
-
-  size_t ASTRecordLayoutsSize = 0;
-
-  size_t MemoizedTypeInfoSize = 0;
-
-  size_t MemoizedUnadjustedAlignSize = 0;
-
-  size_t KeyFunctionsSize = 0;
-
-  size_t BlockVarCopyInitsSize = 0;
-
-  size_t MSGuidDeclsSize = 0;
-
-  size_t UnnamedGlobalConstantDeclsSize = 0;
-
-  size_t TemplateParamObjectDeclsSize = 0;
-
-  size_t StringLiteralCacheSize = 0;
-
-  size_t DestroyingOperatorDeletesSize = 0;
-  size_t TypeAwareOperatorNewAndDeletesSize = 0;
-
-  size_t OperatorDeletesForVirtualDtorSize = 0;
-
-  size_t GlobalOperatorDeletesForVirtualDtorSize = 0;
-
-  size_t ArrayOperatorDeletesForVirtualDtorSize = 0;
-  size_t GlobalArrayOperatorDeletesForVirtualDtorSize = 0;
-
-  size_t MaybeRequireVectorDeletingDtorSize = 0;
-
-  size_t MergedDeclsSize = 0;
-  size_t DeclAttrsSize = 0;
-  size_t MergedDefModulesSize = 0;
-
-  size_t ModuleInitializersSize = 0;
-  size_t PrimaryModuleNameMapSize = 0;
-  size_t SameModuleLookupSetSize = 0;
-
-  size_t ScalableVecTyMapSize = 0;
-  size_t LambdaCastPathsSize = 0;
-  size_t RawCommentsSize = 0;
-  size_t RedeclChainCommentsSize = 0;
-  size_t CommentlessRedeclChainsSize = 0;
-  size_t ParsedCommentsSize = 0;
-  size_t RelocatableClassesSize = 0;
-  size_t ParamIndicesSize = 0;
-  size_t MangleNumbersSize = 0;
-  size_t StaticLocalNumbersSize = 0;
-  size_t TemplateOrInstantiationSize = 0;
-
-  size_t InstantiatedFromUsingDeclSize = 0;
-  size_t InstantiatedFromUsingEnumDeclSize = 0;
-
-  size_t InstantiatedFromUsingShadowDeclSize = 0;
-
-  size_t InstantiatedFromUnnamedFieldDeclSize = 0;
-  size_t OverriddenMethodsSize = 0;
-  size_t MangleNumberingContextsSize = 0;
-  size_t ExtraMangleNumberingContextsSize = 0;
-
-  size_t TraversalScopeSize = 0;
-
-  /// object-c
-  size_t ObjCObjectTypesSize = 0;
-  size_t ObjCObjectPointerTypesSize = 0;
-
-  mutable TypedefDecl *ObjCIdDeclCP = nullptr;
-
-  mutable TypedefDecl *ObjCSelDeclCP = nullptr;
-
-  mutable TypedefDecl *ObjCClassDeclCP = nullptr;
-
-  mutable ObjCInterfaceDecl *ObjCProtocolClassDeclCP = nullptr;
-
-  // llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
-  //    = llvm::PointerIntPair<StoredDeclsMap *, 1>(nullptr, 0);
-  mutable QualType AutoDeductTy;     // Deduction against 'auto'.
-  mutable QualType AutoRRefDeductTy; // Deduction against 'auto &&'.
-
-  // mutable DeclarationNameTable DeclarationNames; need to revert.
-  struct DeclarationNamesCP {
-    size_t CXXConstructorNamesSize = 0;
-    size_t CXXDestructorNamesSize = 0;
-    size_t CXXConversionFunctionNamesSize = 0;
-    detail::CXXOperatorIdName CXXOperatorNamesCP[NUM_OVERLOADED_OPERATORS];
-
-    size_t CXXLiteralOperatorNamesSize = 0;
-
-    size_t CXXDeductionGuideNamesSize = 0;
-  };
-
-  DeclarationNamesCP DeclarationNames;
-};
-
-class ASTContextStateRecovery {
-private:
-  ASTContext &Ctx;
-
-public:
-  explicit ASTContextStateRecovery(ASTContext &Ctx) : Ctx(Ctx) {}
-
-  ASTContextStateRecovery(const ASTContextStateRecovery &) = delete;
-  ASTContextStateRecovery &operator=(const ASTContextStateRecovery &) = delete;
-
-  void stash(StashCheckPoint &CP);
-  void restore(StashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
-  void commit();
-};
-
 /// Index of a per PTU State.
 using PTUID = unsigned;
 
@@ -572,8 +269,77 @@ struct MemberSpecializationFootprint {
   }
 };
 
-class ASTStateReader {
-public:
+class DeclStateReverter {
+private:
+  struct TemplateCommonAccess : RedeclarableTemplateDecl {
+    using RedeclarableTemplateDecl::Common;
+  };
+
+  static const void *rawCommonPtr(const RedeclarableTemplateDecl &RT) {
+    return static_cast<const TemplateCommonAccess &>(RT).Common;
+  }
+
+  // Common is `mutable`, so this is legal to call through a const
+  // reference too -- no const_cast needed at any call site.
+  static void clearCommonPtr(const RedeclarableTemplateDecl &RT) {
+    static_cast<const TemplateCommonAccess &>(RT).Common = nullptr;
+  }
+
+  struct ClassTemplateCommonAccess : ClassTemplateDecl {
+    using ClassTemplateDecl::getCommonPtr;
+  };
+
+  static bool canonInjectedTSTValid(const ClassTemplateDecl &CTD) {
+    auto *Ptr =
+        static_cast<const ClassTemplateCommonAccess &>(CTD).getCommonPtr();
+    return !Ptr->CanonInjectedTST.isNull();
+  }
+
+  static void clearCanonInjectedTST(ClassTemplateDecl &CTD) {
+    auto *Ptr = static_cast<ClassTemplateCommonAccess &>(CTD).getCommonPtr();
+    Ptr->CanonInjectedTST = CanQualType();
+  }
+
+  // DeclContext::FirstDecl/LastDecl -- protected.
+  struct DeclContextLinkAccess : DeclContext {
+    using DeclContext::FirstDecl;
+    using DeclContext::LastDecl;
+  };
+
+  static void clearDeclContextChain(DeclContext &DC) {
+    auto &Access = static_cast<DeclContextLinkAccess &>(DC);
+    Access.FirstDecl = nullptr;
+    Access.LastDecl = nullptr;
+  }
+
+  struct TagDeclDefinitionAccess : TagDecl {
+    using TagDecl::setBeingDefined;
+  };
+
+  static void clearBeingDefined(TagDecl &TD) {
+    static_cast<TagDeclDefinitionAccess &>(TD).setBeingDefined(false);
+  }
+
+  struct DeclLexicalLinkAccess : Decl {
+    using Decl::NextInContextAndBits;
+  };
+
+  static void clearNextInContext(Decl &D) {
+    static_cast<DeclLexicalLinkAccess &>(D).NextInContextAndBits.setPointer(
+        nullptr);
+  }
+
+  template <typename decl_type>
+  struct RedeclLinkAccess : Redeclarable<decl_type> {
+    using Redeclarable<decl_type>::RedeclLink;
+  };
+
+  template <typename decl_type>
+  static void setLatestRedecl(Redeclarable<decl_type> &D, decl_type *Latest) {
+    static_cast<RedeclLinkAccess<decl_type> &>(D).RedeclLink.setLatest(Latest);
+  }
+
+protected:
   static DefinitionDataFootprint *
   createDefinitionDataFootprint(const ASTContext &Ctx, const CXXRecordDecl &RD);
 
@@ -585,8 +351,12 @@ class ASTStateReader {
 
   static SpecializationFootprint *
   createSpecializationFootprint(const ASTContext &Ctx,
-                                const ClassTemplateSpecializationDecl &Spec);
-
+                                const ClassTemplateSpecializationDecl &Spec) {
+    auto *FP =
+        new (Ctx, alignof(SpecializationFootprint)) SpecializationFootprint();
+    FP->update(Spec);
+    return FP;
+  }
   static bool
   compareSpecializationFootprint(const SpecializationFootprint &FP,
                                  const ClassTemplateSpecializationDecl &Spec) {
@@ -602,8 +372,12 @@ class ASTStateReader {
 
   static VarSpecializationFootprint *
   createVarSpecializationFootprint(const ASTContext &Ctx,
-                                   const VarTemplateSpecializationDecl &Spec);
-
+                                   const VarTemplateSpecializationDecl &Spec) {
+    auto *FP = new (Ctx, alignof(VarSpecializationFootprint))
+        VarSpecializationFootprint();
+    FP->update(Spec);
+    return FP;
+  }
   static bool
   compareVarSpecializationFootprint(const VarSpecializationFootprint &FP,
                                     const VarTemplateSpecializationDecl &Spec) {
@@ -619,11 +393,18 @@ class ASTStateReader {
 
   static FunctionSpecializationFootprint *
   createFunctionSpecializationFootprint(const ASTContext &Ctx,
-                                        const FunctionDecl &FD);
-
+                                        const FunctionDecl &FD) {
+    auto *FP = new (Ctx, alignof(FunctionSpecializationFootprint))
+        FunctionSpecializationFootprint();
+    FP->update(FD);
+    return FP;
+  }
   static bool compareFunctionSpecializationFootprint(
-      const FunctionSpecializationFootprint &FP, const FunctionDecl &FD);
-
+      const FunctionSpecializationFootprint &FP, const FunctionDecl &FD) {
+    FunctionSpecializationFootprint Live;
+    Live.update(FD);
+    return FP == Live;
+  }
   static void restoreFunctionSpecializationFootprint(
       const FunctionSpecializationFootprint &FP, FunctionDecl &FD) {
     FP.restore(FD);
@@ -631,32 +412,115 @@ class ASTStateReader {
 
   template <typename OwnerT>
   static MemberSpecializationFootprint *
-  createMemberSpecializationFootprint(const ASTContext &Ctx, const OwnerT &D);
-
+  createMemberSpecializationFootprint(const ASTContext &Ctx, const OwnerT &D) {
+    auto *FP = new (Ctx, alignof(MemberSpecializationFootprint))
+        MemberSpecializationFootprint();
+    FP->update(D);
+    return FP;
+  }
   template <typename OwnerT>
   static bool
   compareMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
-                                       const OwnerT &D);
+                                       const OwnerT &D) {
+    MemberSpecializationFootprint Live;
+    Live.update(D);
+    return FP == Live;
+  }
   template <typename OwnerT>
   static void
   restoreMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
-                                       OwnerT &D);
+                                       OwnerT &D) {
+    FP.restore(D);
+  }
 
   static void restoreDefinitionAndRevertDC(CXXRecordDecl &RD);
 
   static void revertDefinitionArrival(Decl &D);
 
-  static const void *getRawCommonPtr(const RedeclarableTemplateDecl &RT);
+  // True if Common could still be created later -- i.e. nobody has called
+  // getCommonPtr() anywhere in this template's redecl chain yet.
+  static bool needToTrackCommonPtr(const RedeclarableTemplateDecl &RT) {
+    return !rawCommonPtr(RT);
+  }
 
-  static bool isTemplateCanonInjectedTSTValid(const ClassTemplateDecl *CTD);
+  // True if CanonInjectedTST could still be cached later. Only valid to
+  // call once Common itself is confirmed to exist (see needToTrackCommonPtr).
+  static bool
+  needToTrackTemplateCanonInjectedTST(const ClassTemplateDecl *CTD) {
+    return !canonInjectedTSTValid(*CTD);
+  }
+
+  // Write-once fields, fixed null prior state (Common/CanonInjectedTST
+  // never existed before whichever PTU created them), so revert is a
+  // direct reset, not a snapshot restore.
+  static void resetTemplateCommonBase(RedeclarableTemplateDecl &RT) {
+    clearCommonPtr(RT);
+  }
+
+  static void resetCanonInjectedTST(ClassTemplateDecl &CTD) {
+    clearCanonInjectedTST(CTD);
+  }
+
+  // static const Type *getRawTypeForDecl(const TypeDecl *TD) {
+  //   return TD->TypeForDecl;
+  // }
+
+  // static bool needToTrackTypeForDecl(const TypeDecl *TD) {
+  //   const Type *T = getRawTypeForDecl(TD);
+  //   return !T || T->isCanonicalUnqualified();
+  // }
 
-  static void resetTemplateCommonBase(RedeclarableTemplateDecl &RT);
+  // static void resetTypeForDecl(TypeDecl *TD) { TD->TypeForDecl = nullptr; }
 
-  static void resetCanonInjectedTST(ClassTemplateDecl &CTD);
+private:
+  ASTContext &Ctx;
+  llvm::SlabCheckPoint SlabCP;
+  llvm::SmallPtrSet<const DeclContext *, 8> RepairedLexicalContexts;
 
-  static const Type *getRawTypeForDecl(const TypeDecl *TD);
+  bool isAfterCP(const void *P) const {
+    return Ctx.getAllocator().isAfterCheckpoint(P, SlabCP);
+  }
+
+public:
+  DeclStateReverter(ASTContext &Ctx, llvm::SlabCheckPoint CP)
+      : Ctx(Ctx), SlabCP(CP) {}
+
+  static bool isExtensibleContainer(const Decl *D) {
+    return isa<NamespaceDecl>(D) || isa<CXXRecordDecl>(D);
+  }
+
+  static bool isRedeclarableOrOnlyDecl(Decl *D) {
+    return D->getPreviousDecl() != nullptr;
+  }
 
-  static void resetTypeForDecl(TypeDecl *TD);
+  void detachDefData(const Decl *D);
+
+  void detachCommonBase(const Decl *D);
+
+  /// Remove D from its semantic context's lookup map, reinstating the
+  /// previous declaration if D had replaced one in-place (which is what
+  /// StoredDeclsList::HandleRedeclaration does on a redeclaration --
+  /// erasing the slot outright would lose the older decl entirely; that
+  /// is the ReopenNs failure).
+  void detachFromDCLookup(const Decl *D);
+
+  // Walk D's redecl chain looking for the newest decl that predates this
+  // PTU. Returns nullptr if the entire chain was created this PTU.
+  template <typename DeclT> DeclT *findSurvivor(DeclT *D) const;
+
+  template <typename decl_type>
+  void patchRedeclLink(Redeclarable<decl_type> *D, decl_type *Survivor) {
+    setLatestRedecl<decl_type>(*D->getFirstDecl(), Survivor);
+  }
+
+  /// Point the canonical decl's "most recent" link back at the newest
+  /// redeclaration that predates this PTU.
+  void detachFromRedeclChain(const Decl *D);
+
+  void repairLexicalChain(DeclContext &DC);
+
+private:
+  NamedDecl *tryDetachRedeclChain(Decl *D);
 };
 
 template <typename DataT> struct Snapshot {
@@ -714,12 +578,6 @@ using SpecializationChain = StateAwareChain<SpecializationFootprint>;
 using VarSpecializationChain = StateAwareChain<VarSpecializationFootprint>;
 using FunctionSpecializationChain =
     StateAwareChain<FunctionSpecializationFootprint>;
-// One chain type shared by all four MemberSpecializationInfo-backed owner
-// kinds (Function/Var/Record/Enum) -- the footprint shape is identical
-// across all four (see MemberSpecializationFootprint's own comment), so
-// unlike the three chains above, a single alias is enough; what varies is
-// only the DenseMap key type in IncrementalStateTracker
-// (FunctionDecl*/VarDecl*/ CXXRecordDecl*/EnumDecl*), one map per owner kind.
 using MemberSpecializationChain =
     StateAwareChain<MemberSpecializationFootprint>;
 
@@ -782,8 +640,6 @@ class PTUCheckpointLedger {
   }
 };
 
-/// need to handle this     TagDeclBitfields TagDeclBits;
-
 template <typename ValueT> struct FieldMutation {
   PTUID ID;
   ValueT OldValue;
@@ -848,14 +704,8 @@ template <typename OwnerT, typename ValueT> class FieldMutationChain {
   void forget(const OwnerT *Owner) { Log.erase(Owner); }
 };
 
-// struct FunctionTypeEntry {
-//   QualType Type;
-//   QualType OverridingType;
-// };
 using FunctionExceptionSpecChain = FieldMutationChain<FunctionDecl, QualType>;
 using TypeForDeclChain = FieldMutationChain<TagDecl, const Type *>;
-// using CanonInjectedTSTChain =
-//     FieldMutationChain<ClassTemplateDecl, CanQualType>;
 
 struct MutationRecord {
   enum class DeclShape : uint16_t {
@@ -908,12 +758,6 @@ struct PTUStateInfo {
 
   llvm::MapVector<const Decl *, MutationRecord> Mutations;
 
-  // struct CreationRecord {
-  //   DeclShape S;
-  //   CreatedType Type;
-  // };
-
-  // llvm::MapVector<const Decl *, CreationRecord> CreatedDecls;
   llvm::SmallPtrSet<const Decl *, 4> ImplicitDecls;
 
   /// here touched info mean other this belongs to other PTUs;
@@ -1201,7 +1045,6 @@ class IncrementalStateTracker {
 
   FunctionExceptionSpecChain FunctionTypeMutations;
   TypeForDeclChain TagdeclInfos;
-  // CanonInjectedTSTChain;
 
   friend class PTUMutationActions;
 
@@ -1362,6 +1205,7 @@ class IncrementalStateTracker {
   }
 
   PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
+
   bool isFromThisPTU(const void *Ptr, PTUID ID) {
     return PTUSlabCheckpoints.isFromThisPTU(Ctx, Ptr, ID);
   }
@@ -1373,10 +1217,9 @@ class IncrementalStateTracker {
     return It->second;
   }
 
-  // Pops this PTU's own tail entry (if any) from *Field. If the chain is
-  // now empty -- meaning everything it ever held belonged to the PTU being
-  // rolled back -- releases it back to its pool and nulls the pointer.
-  // Returns whether the chain still has surviving (earlier-PTU) history.
+  void undoLastEntries();
+
+private:
   template <typename ChainT>
   static bool rollbackChainField(ChainT *&Field, PTUID ID,
                                  LinkedDeclNodeGenerator &Gen);
@@ -1387,7 +1230,6 @@ class IncrementalStateTracker {
       PTUID ID, LinkedDeclNodeGenerator &Gen);
   void removeLinkedDecl(const Decl *D, PTUID ID);
   // just only remove entries from current();
-  void undoLastEntries();
 };
 
 class PTUMutationActions {
@@ -1468,7 +1310,8 @@ class PTUMutationActions {
   /// EvaluatedValueCached likewise) is never returned as verified --
   /// there is nothing to compare it against, so it cannot be confirmed,
   /// full stop, not assumed either way.
-  // static uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds,
+  // static uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t
+  // FlaggedKinds,
   //                            PTUID ID);
 
   void commitLevel1(PTUID ID, const Decl *D, MutationRecord &Rec,
@@ -1588,13 +1431,11 @@ class PTUMutationRecorder : public ASTMutationListener {
   /// A templated variable's definition was implicitly instantiated.
   void VariableDefinitionInstantiated(const VarDecl *D) override {
     noteDefinitionInstantiated(D);
-    // handle Memberinfo
   }
 
   /// A function template's definition was instantiated.
   void FunctionDefinitionInstantiated(const FunctionDecl *D) override {
     noteDefinitionInstantiated(D);
-    // handle Memberinfo
   }
 
   /// A default argument was instantiated.
@@ -1678,8 +1519,6 @@ class PTUMutationRecorder : public ASTMutationListener {
   /// \param AnonNamespace The anonymous namespace that was added
   void AddedAnonymousNamespace(const TranslationUnitDecl *TU,
                                NamespaceDecl *AnonNamespace) override {}
-
-  void AddedTagDeclType(const TagDecl *TD, const Type *T) override {}
 };
 } // end namespace clang
 #endif // LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7e20dbf371b20..e33073c652dba 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -186,7 +186,6 @@ class SemaSystemZ;
 class SemaWasm;
 class SemaX86;
 class StandardConversionSequence;
-class SemaStateRecovery;
 class TemplateArgument;
 class TemplateArgumentLoc;
 class TemplateInstantiationCallback;
@@ -1588,7 +1587,6 @@ class Sema final : public SemaBase {
   friend class ASTReader;
   friend class ASTDeclReader;
   friend class ASTWriter;
-  friend class SemaStateRecovery;
 
 private:
   std::optional<std::unique_ptr<DarwinSDKInfo>> CachedDarwinSDKInfo;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index f0d760845d3b4..a361b07e201b3 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5596,10 +5596,6 @@ CanQualType ASTContext::getCanonicalTagType(const TagDecl *TD) const {
       /*OwnsTag=*/false, /*IsInjected=*/false, /*CanonicalType=*/nullptr,
       /*WithFoldingSetNode=*/false);
   TD->TypeForDecl = CanonicalType;
-
-  if (ASTMutationListener *L = getASTMutationListener())
-    L->AddedTagDeclType(TD, TD->TypeForDecl);
-
   return CanQualType::CreateUnsafe(QualType(CanonicalType, 0));
 }
 
@@ -5625,12 +5621,7 @@ QualType ASTContext::getTagType(ElaboratedTypeKeyword Keyword,
                            /*Qualifier=*/std::nullopt, NonInjectedTD,
                            /*OwnsTag=*/false, IsInjected, CanonicalType,
                            /*WithFoldingSetNode=*/false);
-
     TD->TypeForDecl = T;
-
-    if (ASTMutationListener *L = getASTMutationListener())
-      L->AddedTagDeclType(TD, TD->TypeForDecl);
-
     return QualType(T, 0);
   }
 
diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp
index bd9f4af9b7fd2..4d27b04248f93 100644
--- a/clang/lib/Frontend/MultiplexConsumer.cpp
+++ b/clang/lib/Frontend/MultiplexConsumer.cpp
@@ -130,7 +130,6 @@ class MultiplexASTMutationListener : public ASTMutationListener {
   void AddedStaticLocalNumbers(const Decl *D, unsigned) override;
   void AddedAnonymousNamespace(const TranslationUnitDecl *,
                                NamespaceDecl *AnonNamespace) override;
-  void AddedTagDeclType(const TagDecl *TD, const Type *T) override;
 
 private:
   std::vector<ASTMutationListener*> Listeners;
@@ -279,10 +278,6 @@ void MultiplexASTMutationListener::AddedAnonymousNamespace(
   for (auto *L : Listeners)
     L->AddedAnonymousNamespace(TU, AnonNamespace);
 }
-void MultiplexASTMutationListener::AddedTagDeclType(const TagDecl *TD, const Type *T) {
-  for (auto *L : Listeners)
-    L->AddedTagDeclType(TD, T);
-}
 
 }  // end namespace clang
 
diff --git a/clang/lib/Interpreter/ASTContextStateStash.cpp b/clang/lib/Interpreter/ASTContextStateStash.cpp
deleted file mode 100644
index d9da3f7448da8..0000000000000
--- a/clang/lib/Interpreter/ASTContextStateStash.cpp
+++ /dev/null
@@ -1,1808 +0,0 @@
-//===--- ASTContextStateRecovery.cpp - ASTContext persistent state stash/restore
-//----------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Interpreter/ErrorRecovery.h"
-
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/DeclContextInternals.h"
-#include "clang/AST/DeclTemplate.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/ExprCXX.h"
-#include "clang/AST/MangleNumberingContext.h"
-#include "clang/AST/RecordLayout.h"
-#include "clang/AST/Type.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include "llvm/ADT/SmallSet.h"
-#include "llvm/ADT/SmallVector.h"
-
-namespace clang {
-
-/// Erases nodes from a FoldingSet based on a predicate.
-template <typename EntryType, typename PredT>
-static void eraseFoldingSetIf(llvm::UniquingSet<EntryType> &FS, PredT &&Pred) {
-  SmallVector<EntryType *, 16> ToRemove;
-  for (auto &N : FS)
-    if (Pred(N))
-      ToRemove.push_back(&N);
-
-  for (auto *N : ToRemove)
-    FS.erase(N);
-}
-
-template <typename EntryType, typename UniquingSetInfo, typename PredT>
-static void eraseFoldingSetIf(llvm::UniquingSet<EntryType, UniquingSetInfo> &FS,
-                              PredT &&Pred) {
-  SmallVector<EntryType *, 16> ToRemove;
-  for (auto &N : FS)
-    if (Pred(N))
-      ToRemove.push_back(&N);
-
-  for (auto *N : ToRemove)
-    FS.erase(N);
-}
-
-template <typename EntryType, typename PredT>
-static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
-  SmallVector<EntryType *, 16> ToRemove;
-  for (auto &N : FS)
-    if (Pred(N))
-      ToRemove.push_back(&N);
-
-  for (auto *N : ToRemove)
-    FS.erase(N);
-}
-
-template <typename EntryType, typename PredT>
-static void
-eraseFoldingSetIf(llvm::ContextualFoldingSet<EntryType, ASTContext &> &FS,
-                  PredT &&Pred) {
-  SmallVector<EntryType *, 16> ToRemove;
-
-  for (auto &N : FS)
-    if (Pred(N))
-      ToRemove.push_back(&N);
-
-  for (auto *N : ToRemove)
-    FS.erase(N);
-}
-
-/// Erase from DenseMap based on predicate
-template <typename KeyT, typename ValueT, typename PredT>
-static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
-  SmallVector<KeyT, 16> ToRemove;
-  for (auto &KV : Map)
-    if (Pred(KV))
-      ToRemove.push_back(KV.getFirst());
-
-  for (auto &Key : ToRemove)
-    Map.erase(Key);
-}
-
-/// Erase from DenseMap based on predicate
-template <typename ValueT, typename PredT>
-static void eraseDenseSetIf(llvm::DenseSet<ValueT> &Set, PredT &&Pred) {
-  SmallVector<ValueT, 16> ToRemove;
-
-  for (auto &V : Set)
-    if (Pred(V))
-      ToRemove.push_back(V);
-
-  for (auto &Key : ToRemove)
-    Set.erase(Key);
-}
-
-void ASTContextStateRecovery::stash(StashCheckPoint &CP) {
-  CP.TypesSize = Ctx.Types.size();
-  CP.ExtQualNodesSize = Ctx.ExtQualNodes.size();
-  CP.ComplexTypesSize = Ctx.ComplexTypes.size();
-  CP.PointerTypesSize = Ctx.PointerTypes.size();
-  CP.AdjustedTypesSize = Ctx.AdjustedTypes.size();
-  CP.BlockPointerTypesSize = Ctx.BlockPointerTypes.size();
-  CP.LValueReferenceTypesSize = Ctx.LValueReferenceTypes.size();
-  CP.RValueReferenceTypesSize = Ctx.RValueReferenceTypes.size();
-  CP.MemberPointerTypesSize = Ctx.MemberPointerTypes.size();
-
-  CP.ConstantArrayTypesSize = Ctx.ConstantArrayTypes.size();
-  CP.IncompleteArrayTypesSize = Ctx.IncompleteArrayTypes.size();
-  CP.VariableArrayTypesSize = Ctx.VariableArrayTypes.size();
-
-  CP.DependentSizedArrayTypesSize = Ctx.DependentSizedArrayTypes.size();
-  CP.DependentSizedExtVectorTypesSize = Ctx.DependentSizedExtVectorTypes.size();
-  CP.DependentAddressSpaceTypesSize = Ctx.DependentAddressSpaceTypes.size();
-  CP.VectorTypesSize = Ctx.VectorTypes.size();
-  CP.DependentVectorTypesSize = Ctx.DependentVectorTypes.size();
-  CP.MatrixTypesSize = Ctx.MatrixTypes.size();
-  CP.DependentSizedMatrixTypesSize = Ctx.DependentSizedMatrixTypes.size();
-  CP.FunctionNoProtoTypesSize = Ctx.FunctionNoProtoTypes.size();
-  CP.FunctionProtoTypesSize = Ctx.FunctionProtoTypes.size();
-  CP.DependentTypeOfExprTypesSize = Ctx.DependentTypeOfExprTypes.size();
-  CP.DependentDecltypeTypesSize = Ctx.DependentDecltypeTypes.size();
-
-  CP.DependentPackIndexingTypesSize = Ctx.DependentPackIndexingTypes.size();
-
-  CP.TemplateTypeParmTypesSize = Ctx.TemplateTypeParmTypes.size();
-  CP.ObjCTypeParamTypesSize = Ctx.ObjCTypeParamTypes.size();
-  CP.SubstTemplateTypeParmTypesSize = Ctx.SubstTemplateTypeParmTypes.size();
-  CP.SubstTemplateTypeParmPackTypesSize =
-      Ctx.SubstTemplateTypeParmPackTypes.size();
-  CP.SubstBuiltinTemplatePackTypesSize =
-      Ctx.SubstBuiltinTemplatePackTypes.size();
-
-  CP.TemplateSpecializationTypesSize = Ctx.TemplateSpecializationTypes.size();
-  CP.ParenTypesSize = Ctx.ParenTypes.size();
-  CP.TagTypesSize = Ctx.TagTypes.size();
-  CP.UnresolvedUsingTypesSize = Ctx.UnresolvedUsingTypes.size();
-  CP.UsingTypesSize = Ctx.UsingTypes.size();
-  CP.TypedefTypesSize = Ctx.TypedefTypes.size();
-  CP.DependentNameTypesSize = Ctx.DependentNameTypes.size();
-  CP.PackExpansionTypesSize = Ctx.PackExpansionTypes.size();
-  CP.ObjCObjectTypesSize = Ctx.ObjCObjectTypes.size();
-  CP.ObjCObjectPointerTypesSize = Ctx.ObjCObjectPointerTypes.size();
-  CP.UnaryTransformTypesSize = Ctx.UnaryTransformTypes.size();
-
-  CP.AutoTypesSize = Ctx.AutoTypes.size();
-  CP.DeducedTemplateSpecializationTypesSize =
-      Ctx.DeducedTemplateSpecializationTypes.size();
-  CP.AtomicTypesSize = Ctx.AtomicTypes.size();
-  CP.AttributedTypesSize = Ctx.AttributedTypes.size();
-  CP.PipeTypesSize = Ctx.PipeTypes.size();
-  CP.BitIntTypesSize = Ctx.BitIntTypes.size();
-  CP.DependentBitIntTypesSize = Ctx.DependentBitIntTypes.size();
-  CP.BTFTagAttributedTypesSize = Ctx.BTFTagAttributedTypes.size();
-  CP.HLSLAttributedResourceTypesSize = Ctx.HLSLAttributedResourceTypes.size();
-  CP.HLSLInlineSpirvTypesSize = Ctx.HLSLInlineSpirvTypes.size();
-
-  CP.CountAttributedTypesSize = Ctx.CountAttributedTypes.size();
-
-  CP.QualifiedTemplateNamesSize = Ctx.QualifiedTemplateNames.size();
-  CP.DependentTemplateNamesSize = Ctx.DependentTemplateNames.size();
-  CP.SubstTemplateTemplateParmsSize = Ctx.SubstTemplateTemplateParms.size();
-  CP.SubstTemplateTemplateParmPacksSize =
-      Ctx.SubstTemplateTemplateParmPacks.size();
-  CP.DeducedTemplatesSize = Ctx.DeducedTemplates.size();
-
-  CP.ArrayParameterTypesSize = Ctx.ArrayParameterTypes.size();
-
-  CP.PredefinedSugarTypesSize = Ctx.PredefinedSugarTypes.size();
-
-  CP.NamespaceAndPrefixStoragesSize = Ctx.NamespaceAndPrefixStorages.size();
-
-  CP.ASTRecordLayoutsSize = Ctx.ASTRecordLayouts.size();
-
-  CP.MemoizedTypeInfoSize = Ctx.MemoizedTypeInfo.size();
-
-  CP.MemoizedUnadjustedAlignSize = Ctx.MemoizedUnadjustedAlign.size();
-
-  CP.KeyFunctionsSize = Ctx.KeyFunctions.size();
-
-  CP.BlockVarCopyInitsSize = Ctx.BlockVarCopyInits.size();
-
-  CP.MSGuidDeclsSize = Ctx.MSGuidDecls.size();
-
-  CP.UnnamedGlobalConstantDeclsSize = Ctx.UnnamedGlobalConstantDecls.size();
-
-  CP.TemplateParamObjectDeclsSize = Ctx.TemplateParamObjectDecls.size();
-
-  CP.StringLiteralCacheSize = Ctx.StringLiteralCache.size();
-
-  CP.DestroyingOperatorDeletesSize = Ctx.DestroyingOperatorDeletes.size();
-  CP.TypeAwareOperatorNewAndDeletesSize =
-      Ctx.TypeAwareOperatorNewAndDeletes.size();
-
-  CP.OperatorDeletesForVirtualDtorSize =
-      Ctx.OperatorDeletesForVirtualDtor.size();
-
-  CP.GlobalOperatorDeletesForVirtualDtorSize =
-      Ctx.GlobalOperatorDeletesForVirtualDtor.size();
-
-  CP.ArrayOperatorDeletesForVirtualDtorSize =
-      Ctx.ArrayOperatorDeletesForVirtualDtor.size();
-  CP.GlobalArrayOperatorDeletesForVirtualDtorSize =
-      Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size();
-
-  CP.MaybeRequireVectorDeletingDtorSize =
-      Ctx.MaybeRequireVectorDeletingDtor.size();
-
-  CP.MergedDeclsSize = Ctx.MergedDecls.size();
-  CP.DeclAttrsSize = Ctx.DeclAttrs.size();
-  CP.MergedDefModulesSize = Ctx.MergedDefModules.size();
-
-  CP.ModuleInitializersSize = Ctx.ModuleInitializers.size();
-  CP.PrimaryModuleNameMapSize = Ctx.PrimaryModuleNameMap.size();
-  CP.SameModuleLookupSetSize = Ctx.SameModuleLookupSet.size();
-
-  CP.ScalableVecTyMapSize = Ctx.ScalableVecTyMap.size();
-  CP.LambdaCastPathsSize = Ctx.LambdaCastPaths.size();
-  CP.RawCommentsSize = Ctx.RawComments.size();
-  CP.RedeclChainCommentsSize = Ctx.RedeclChainComments.size();
-  CP.CommentlessRedeclChainsSize = Ctx.CommentlessRedeclChains.size();
-  CP.ParsedCommentsSize = Ctx.ParsedComments.size();
-  CP.RelocatableClassesSize = Ctx.RelocatableClasses.size();
-  CP.ParamIndicesSize = Ctx.ParamIndices.size();
-  CP.MangleNumbersSize = Ctx.MangleNumbers.size();
-  CP.StaticLocalNumbersSize = Ctx.StaticLocalNumbers.size();
-  CP.TemplateOrInstantiationSize = Ctx.TemplateOrInstantiation.size();
-
-  CP.InstantiatedFromUsingDeclSize = Ctx.InstantiatedFromUsingDecl.size();
-  CP.InstantiatedFromUsingEnumDeclSize =
-      Ctx.InstantiatedFromUsingEnumDecl.size();
-
-  CP.InstantiatedFromUsingShadowDeclSize =
-      Ctx.InstantiatedFromUsingShadowDecl.size();
-
-  CP.InstantiatedFromUnnamedFieldDeclSize =
-      Ctx.InstantiatedFromUnnamedFieldDecl.size();
-  CP.OverriddenMethodsSize = Ctx.OverriddenMethods.size();
-  CP.MangleNumberingContextsSize = Ctx.MangleNumberingContexts.size();
-  CP.ExtraMangleNumberingContextsSize = Ctx.ExtraMangleNumberingContexts.size();
-  CP.TraversalScopeSize = Ctx.TraversalScope.size();
-  // CP.LastSDM = Ctx.LastSDM;
-  CP.AutoDeductTy = Ctx.AutoDeductTy;         // Deduction against 'auto'.
-  CP.AutoRRefDeductTy = Ctx.AutoRRefDeductTy; // Deduction against 'auto &&'.
-
-  /// ObjectC
-  CP.ObjCObjectTypesSize = Ctx.ObjCObjectTypes.size();
-  CP.ObjCObjectPointerTypesSize = Ctx.ObjCObjectPointerTypes.size();
-  CP.ObjCIdDeclCP = Ctx.ObjCIdDecl;
-
-  CP.ObjCSelDeclCP = Ctx.ObjCSelDecl;
-
-  CP.ObjCClassDeclCP = Ctx.ObjCClassDecl;
-
-  CP.ObjCProtocolClassDeclCP = Ctx.ObjCProtocolClassDecl;
-
-  /// DeclarationNameTable
-  CP.DeclarationNames.CXXConstructorNamesSize =
-      Ctx.DeclarationNames.CXXConstructorNames.size();
-  CP.DeclarationNames.CXXDestructorNamesSize =
-      Ctx.DeclarationNames.CXXDestructorNames.size();
-  CP.DeclarationNames.CXXConversionFunctionNamesSize =
-      Ctx.DeclarationNames.CXXConversionFunctionNames.size();
-  for (unsigned I = 0; I < NUM_OVERLOADED_OPERATORS; I++)
-    CP.DeclarationNames.CXXOperatorNamesCP[I] =
-        Ctx.DeclarationNames.CXXOperatorNames[I];
-  CP.DeclarationNames.CXXLiteralOperatorNamesSize =
-      Ctx.DeclarationNames.CXXLiteralOperatorNames.size();
-  CP.DeclarationNames.CXXDeductionGuideNamesSize =
-      Ctx.DeclarationNames.CXXDeductionGuideNames.size();
-}
-
-class DeclContextRepairer {
-  ASTContext &Ctx;
-  llvm::SlabCheckPoint SlabCP;
-  // in case to avoid revisit.
-  llvm::SmallPtrSet<const DeclContext *, 8> Visited;
-
-  bool isAfterCP(const void *P) const {
-    return Ctx.getAllocator().isAfterCheckpoint(P, SlabCP);
-  }
-
-  static bool isExtensibleContainer(const Decl *D) {
-    return isa<NamespaceDecl>(D) || isa<TagDecl>(D);
-  }
-
-  static bool isRedeclarableOrOnlyDecl(Decl *D) {
-    return D->getPreviousDecl() != nullptr;
-  }
-
-  // Walk D's redecl chain looking for the newest decl that was from previous
-  // PTU. Returns nullptr if the entire chain was created this PTU.
-  template <typename DeclT> DeclT *findSurvivor(DeclT *D) {
-    for (DeclT *It = D->getMostRecentDecl(); It; It = It->getPreviousDecl()) {
-      if (!isAfterCP(It))
-        return It;
-      if (It == It->getFirstDecl())
-        break;
-    }
-    return nullptr;
-  }
-
-  template <typename decl_type>
-  void patchRedeclLink(Redeclarable<decl_type> *D, decl_type *Survivor) {
-    D->getFirstDecl()->RedeclLink.setLatest(Survivor);
-  }
-
-  void repairLexicalChain(DeclContext &DC) {
-    Decl *Prev = nullptr;
-    for (Decl *Cur = DC.FirstDecl; Cur; Cur = Cur->getNextDeclInContext()) {
-      if (isAfterCP(Cur)) {
-        if (Prev) {
-          DC.LastDecl = Prev;
-          Prev->NextInContextAndBits.setPointer(nullptr);
-        } else {
-          DC.FirstDecl = DC.LastDecl = nullptr; // nothing survives
-        }
-        return;
-      }
-      Prev = Cur;
-    }
-  }
-
-  NamedDecl *tryRepairRedeclChain(Decl *D) {
-    if (!isRedeclarableOrOnlyDecl(D))
-      return nullptr; // nothing to do
-
-    NamedDecl *Survivor = nullptr;
-
-    auto repairRedeclChain = [&](auto *RD) {
-      // using DeclT = std::remove_pointer_t<decltype(RD)>;
-
-      auto *SurvivorDecl = findSurvivor(RD);
-      if (SurvivorDecl)
-        patchRedeclLink(RD, SurvivorDecl);
-
-      Survivor = SurvivorDecl;
-    };
-
-    switch (D->getKind()) {
-    case Decl::Function:
-      repairRedeclChain(cast<FunctionDecl>(D));
-      break;
-
-    case Decl::Var:
-      repairRedeclChain(cast<VarDecl>(D));
-      break;
-
-    case Decl::Enum:
-      repairRedeclChain(cast<TagDecl>(D));
-      break;
-
-    case Decl::Record:
-    case Decl::CXXRecord:
-      repairRedeclChain(cast<TagDecl>(D));
-      break;
-
-    case Decl::ClassTemplate:
-      repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
-      break;
-
-    case Decl::FunctionTemplate:
-      repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
-      break;
-
-    case Decl::TypeAliasTemplate:
-      repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
-      break;
-
-    case Decl::VarTemplate:
-      repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
-      break;
-
-    case Decl::Namespace:
-      repairRedeclChain(cast<NamespaceDecl>(D));
-      break;
-
-    default:
-      break;
-    }
-
-    return Survivor;
-  }
-
-  void repairLookupEntry(StoredDeclsMap &Map, DeclarationName Key,
-                         StoredDeclsList &List, bool Recurse) {
-    // Snapshot first: remove()/addOrReplaceDecl() below invalidate the live
-    // view getLookupResult() returns.
-    SmallVector<NamedDecl *, 4> Snapshot(List.getLookupResult().begin(),
-                                         List.getLookupResult().end());
-
-    for (NamedDecl *D : Snapshot) {
-      if (Recurse && isExtensibleContainer(D))
-        repairDeclContextLookup(cast<DeclContext>(D), Recurse);
-
-      if (!isAfterCP(D))
-        continue; // predates this PTU -- untouched
-
-      NamedDecl *Survivor = tryRepairRedeclChain(D); // may be null
-
-      if (!Survivor) {
-        List.remove(D); // whole chain born this PTU
-        continue;
-      }
-      assert(Survivor != D && "survivor postdates the checkpoint");
-
-      // If the survivor is already visible under this name, D was an extra
-      // entry (an overload); otherwise D had REPLACED the survivor's slot
-      // via HandleRedeclaration and has to hand it back.
-      if (llvm::is_contained(Snapshot, Survivor))
-        List.remove(D);
-      else
-        List.addOrReplaceDecl(Survivor);
-    }
-
-    if (List.isNull())
-      Map.erase(Key);
-  }
-
-public:
-  DeclContextRepairer(ASTContext &Ctx, llvm::SlabCheckPoint CP)
-      : Ctx(Ctx), SlabCP(CP) {}
-
-  template <typename decl_type>
-  void repairRedeclchain(Redeclarable<decl_type> *D) {
-    decl_type *SurviourDecl = findSurvivor(D);
-    if (SurviourDecl)
-      patchRedeclLink(D, SurviourDecl);
-  }
-
-  // Public entry point -- recursivly repair affected DCs.
-  void repairDeclContextLookup(DeclContext *DC, bool NoRecursive = false) {
-    DeclContext *Primary = DC->getPrimaryContext();
-    if (!Visited.insert(Primary).second)
-      return;
-
-    bool CompletelyNew = isAfterCP(cast<Decl>(Primary)->getCanonicalDecl());
-    StoredDeclsMap *Map = Primary->getLookupPtr();
-
-    if (CompletelyNew) {
-      // Primary's very first declaration happened this PTU -- the entire
-      // entity is being discarded as a unit, so there's nothing to
-      // individually repair.
-      if (Map)
-        Map->clear();
-      return;
-    }
-
-    if (Map)
-      for (auto &Entry : *Map)
-        repairLookupEntry(*Map, Entry.first, Entry.second, NoRecursive);
-    repairLexicalChain(*Primary);
-  }
-};
-
-void ASTContextStateRecovery::restore(StashCheckPoint &CP,
-                                      llvm::SlabCheckPoint SlabCP) {
-  if (CP.AutoDeductTy != Ctx.AutoDeductTy) {
-    llvm::dbgs() << "Ctx.AutoDeductTy != CP.AutoDeductTy\n";
-    Ctx.AutoDeductTy = CP.AutoDeductTy;
-  }
-
-  if (CP.AutoRRefDeductTy != Ctx.AutoRRefDeductTy) {
-    llvm::dbgs() << "Ctx.AutoRRefDeductTy != CP.AutoRRefDeductTy\n";
-    Ctx.AutoRRefDeductTy = CP.AutoRRefDeductTy;
-  }
-
-  if (Ctx.TemplateTypeParmTypes.size() != CP.TemplateTypeParmTypesSize) {
-    llvm::dbgs() << "Ctx.TemplateTypeParmTypes.size() != "
-                    "CP.TemplateTypeParmTypesSize\n";
-    eraseFoldingSetIf(Ctx.TemplateTypeParmTypes,
-                      [&](TemplateTypeParmType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.TemplateTypeParmTypes.size() == CP.TemplateTypeParmTypesSize);
-  }
-
-  if (Ctx.TagTypes.size() != CP.TagTypesSize) {
-    llvm::dbgs() << "Ctx.TagTypes.size() != CP.TagTypesSize\n";
-    eraseFoldingSetIf(Ctx.TagTypes,
-                      [&](TagTypeFoldingSetPlaceholder &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-
-    assert(Ctx.TagTypes.size() == CP.TagTypesSize);
-  }
-
-  if (Ctx.ExtQualNodes.size() != CP.ExtQualNodesSize) {
-    llvm::dbgs() << "Ctx.ExtQualNodes.size() != CP.ExtQualNodesSize\n";
-    eraseFoldingSetIf(Ctx.ExtQualNodes, [&](ExtQuals &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.ExtQualNodes.size() == CP.ExtQualNodesSize);
-  }
-
-  if (Ctx.ComplexTypes.size() != CP.ComplexTypesSize) {
-    llvm::dbgs() << "Ctx.ComplexTypes.size() != CP.ComplexTypesSize\n";
-    eraseFoldingSetIf(Ctx.ComplexTypes, [&](ComplexType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.ComplexTypes.size() == CP.ComplexTypesSize);
-  }
-
-  if (Ctx.PointerTypes.size() != CP.PointerTypesSize) {
-    llvm::dbgs() << "Ctx.PointerTypes.size() != CP.PointerTypesSize\n";
-    eraseFoldingSetIf(Ctx.PointerTypes, [&](PointerType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.PointerTypes.size() == CP.PointerTypesSize);
-  }
-
-  if (Ctx.AdjustedTypes.size() != CP.AdjustedTypesSize) {
-    llvm::dbgs() << "Ctx.AdjustedTypes.size() != CP.AdjustedTypesSize\n";
-    eraseFoldingSetIf(Ctx.AdjustedTypes, [&](AdjustedType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.AdjustedTypes.size() == CP.AdjustedTypesSize);
-  }
-
-  if (Ctx.BlockPointerTypes.size() != CP.BlockPointerTypesSize) {
-    llvm::dbgs()
-        << "Ctx.BlockPointerTypes.size() != CP.BlockPointerTypesSize\n";
-    eraseFoldingSetIf(Ctx.BlockPointerTypes,
-                      [&](BlockPointerType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.BlockPointerTypes.size() == CP.BlockPointerTypesSize);
-  }
-
-  if (Ctx.LValueReferenceTypes.size() != CP.LValueReferenceTypesSize) {
-    llvm::dbgs()
-        << "Ctx.LValueReferenceTypes.size() != CP.LValueReferenceTypesSize\n";
-    eraseFoldingSetIf(Ctx.LValueReferenceTypes,
-                      [&](LValueReferenceType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.LValueReferenceTypes.size() == CP.LValueReferenceTypesSize);
-  }
-
-  if (Ctx.RValueReferenceTypes.size() != CP.RValueReferenceTypesSize) {
-    llvm::dbgs()
-        << "Ctx.RValueReferenceTypes.size() != CP.RValueReferenceTypesSize\n";
-    eraseFoldingSetIf(Ctx.RValueReferenceTypes,
-                      [&](RValueReferenceType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.RValueReferenceTypes.size() == CP.RValueReferenceTypesSize);
-  }
-
-  if (Ctx.MemberPointerTypes.size() != CP.MemberPointerTypesSize) {
-    llvm::dbgs()
-        << "Ctx.MemberPointerTypes.size() != CP.MemberPointerTypesSize\n";
-    eraseFoldingSetIf(Ctx.MemberPointerTypes,
-                      [&](MemberPointerType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.MemberPointerTypes.size() == CP.MemberPointerTypesSize);
-  }
-
-  if (Ctx.ConstantArrayTypes.size() != CP.ConstantArrayTypesSize) {
-    llvm::dbgs()
-        << "Ctx.ConstantArrayTypes.size() != CP.ConstantArrayTypesSize\n";
-    eraseFoldingSetIf(Ctx.ConstantArrayTypes,
-                      [&](ConstantArrayType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.ConstantArrayTypes.size() == CP.ConstantArrayTypesSize);
-  }
-
-  if (Ctx.IncompleteArrayTypes.size() != CP.IncompleteArrayTypesSize) {
-    llvm::dbgs()
-        << "Ctx.IncompleteArrayTypes.size() != CP.IncompleteArrayTypesSize\n";
-    eraseFoldingSetIf(Ctx.IncompleteArrayTypes,
-                      [&](IncompleteArrayType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.IncompleteArrayTypes.size() == CP.IncompleteArrayTypesSize);
-  }
-
-  // mutable std::vector<VariableArrayType*> VariableArrayTypes;
-  if (Ctx.VariableArrayTypes.size() != CP.VariableArrayTypesSize) {
-    llvm::dbgs()
-        << "Ctx.VariableArrayTypes.size() != CP.VariableArrayTypesSize\n";
-    Ctx.VariableArrayTypes.resize(CP.VariableArrayTypesSize);
-    assert(Ctx.VariableArrayTypes.size() == CP.VariableArrayTypesSize);
-  }
-
-  if (Ctx.DependentSizedArrayTypes.size() != CP.DependentSizedArrayTypesSize) {
-    llvm::dbgs() << "Ctx.DependentSizedArrayTypes.size() != "
-                    "CP.DependentSizedArrayTypesSize\n";
-    eraseFoldingSetIf(Ctx.DependentSizedArrayTypes,
-                      [&](DependentSizedArrayType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentSizedArrayTypes.size() ==
-           CP.DependentSizedArrayTypesSize);
-  }
-
-  if (Ctx.DependentSizedExtVectorTypes.size() !=
-      CP.DependentSizedExtVectorTypesSize) {
-    llvm::dbgs() << "Ctx.DependentSizedExtVectorTypes.size() != "
-                    "CP.DependentSizedExtVectorTypesSize\n";
-    eraseFoldingSetIf(Ctx.DependentSizedExtVectorTypes,
-                      [&](DependentSizedExtVectorType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentSizedExtVectorTypes.size() ==
-           CP.DependentSizedExtVectorTypesSize);
-  }
-
-  if (Ctx.DependentAddressSpaceTypes.size() !=
-      CP.DependentAddressSpaceTypesSize) {
-    llvm::dbgs() << "Ctx.DependentAddressSpaceTypes.size() != "
-                    "CP.DependentAddressSpaceTypesSize\n";
-
-    eraseFoldingSetIf(Ctx.DependentAddressSpaceTypes,
-                      [&](DependentAddressSpaceType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentAddressSpaceTypes.size() ==
-           CP.DependentAddressSpaceTypesSize);
-  }
-
-  if (Ctx.VectorTypes.size() != CP.VectorTypesSize) {
-    llvm::dbgs() << "Ctx.VectorTypes.size() != CP.VectorTypesSize\n";
-    eraseFoldingSetIf(Ctx.VectorTypes, [&](VectorType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.VectorTypes.size() == CP.VectorTypesSize);
-  }
-
-  if (Ctx.DependentVectorTypes.size() != CP.DependentVectorTypesSize) {
-    llvm::dbgs()
-        << "Ctx.DependentVectorTypes.size() != CP.DependentVectorTypesSize\n";
-    eraseFoldingSetIf(Ctx.DependentVectorTypes,
-                      [&](DependentVectorType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentVectorTypes.size() == CP.DependentVectorTypesSize);
-  }
-
-  if (Ctx.MatrixTypes.size() != CP.MatrixTypesSize) {
-    llvm::dbgs() << "Ctx.MatrixTypes.size() != CP.MatrixTypesSize\n";
-    eraseFoldingSetIf(Ctx.MatrixTypes, [&](ConstantMatrixType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.MatrixTypes.size() == CP.MatrixTypesSize);
-  }
-
-  if (Ctx.DependentSizedMatrixTypes.size() !=
-      CP.DependentSizedMatrixTypesSize) {
-    llvm::dbgs() << "Ctx.DependentSizedMatrixTypes.size() != "
-                    "CP.DependentSizedMatrixTypesSize\n";
-    eraseFoldingSetIf(Ctx.DependentSizedMatrixTypes,
-                      [&](DependentSizedMatrixType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentSizedMatrixTypes.size() ==
-           CP.DependentSizedMatrixTypesSize);
-  }
-
-  if (Ctx.FunctionNoProtoTypes.size() != CP.FunctionNoProtoTypesSize) {
-    llvm::dbgs()
-        << "Ctx.FunctionNoProtoTypes.size() != CP.FunctionNoProtoTypesSize\n";
-    eraseFoldingSetIf(Ctx.FunctionNoProtoTypes,
-                      [&](FunctionNoProtoType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.FunctionNoProtoTypes.size() == CP.FunctionNoProtoTypesSize);
-  }
-
-  if (Ctx.FunctionProtoTypes.size() != CP.FunctionProtoTypesSize) {
-    llvm::dbgs()
-        << "Ctx.FunctionProtoTypes.size() != CP.FunctionProtoTypesSize\n";
-    eraseFoldingSetIf(Ctx.FunctionProtoTypes,
-                      [&](FunctionProtoType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.FunctionProtoTypes.size() == CP.FunctionProtoTypesSize);
-  }
-
-  if (Ctx.DependentTypeOfExprTypes.size() != CP.DependentTypeOfExprTypesSize) {
-    llvm::dbgs() << "Ctx.DependentTypeOfExprTypes.size() != "
-                    "CP.DependentTypeOfExprTypesSize\n";
-    eraseFoldingSetIf(Ctx.DependentTypeOfExprTypes,
-                      [&](DependentTypeOfExprType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentTypeOfExprTypes.size() ==
-           CP.DependentTypeOfExprTypesSize);
-  }
-
-  if (Ctx.DependentDecltypeTypes.size() != CP.DependentDecltypeTypesSize) {
-    llvm::dbgs() << "Ctx.DependentDecltypeTypes.size() != "
-                    "CP.DependentDecltypeTypesSize\n";
-    eraseFoldingSetIf(Ctx.DependentDecltypeTypes,
-                      [&](DependentDecltypeType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentDecltypeTypes.size() == CP.DependentDecltypeTypesSize);
-  }
-
-  if (Ctx.DependentPackIndexingTypes.size() !=
-      CP.DependentPackIndexingTypesSize) {
-    llvm::dbgs() << "Ctx.DependentPackIndexingTypes.size() != "
-                    "CP.DependentPackIndexingTypesSize\n";
-    eraseFoldingSetIf(Ctx.DependentPackIndexingTypes,
-                      [&](PackIndexingType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentPackIndexingTypes.size() ==
-           CP.DependentPackIndexingTypesSize);
-  }
-
-  if (Ctx.TemplateTypeParmTypes.size() != CP.TemplateTypeParmTypesSize) {
-    llvm::dbgs() << "Ctx.TemplateTypeParmTypes.size() != "
-                    "CP.TemplateTypeParmTypesSize\n";
-    eraseFoldingSetIf(Ctx.TemplateTypeParmTypes,
-                      [&](TemplateTypeParmType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.TemplateTypeParmTypes.size() == CP.TemplateTypeParmTypesSize);
-  }
-
-  // mutable llvm::FoldingSet<ObjCTypeParamType> ObjCTypeParamTypes;
-
-  if (Ctx.SubstTemplateTypeParmTypes.size() !=
-      CP.SubstTemplateTypeParmTypesSize) {
-    llvm::dbgs() << "Ctx.SubstTemplateTypeParmTypes.size() != "
-                    "CP.SubstTemplateTypeParmTypesSize\n";
-    eraseFoldingSetIf(Ctx.SubstTemplateTypeParmTypes,
-                      [&](SubstTemplateTypeParmType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.SubstTemplateTypeParmTypes.size() ==
-           CP.SubstTemplateTypeParmTypesSize);
-  }
-
-  if (Ctx.SubstTemplateTypeParmPackTypes.size() !=
-      CP.SubstTemplateTypeParmPackTypesSize) {
-    llvm::dbgs() << "Ctx.SubstTemplateTypeParmPackTypes.size() != "
-                    "CP.SubstTemplateTypeParmPackTypesSize\n";
-    eraseFoldingSetIf(Ctx.SubstTemplateTypeParmPackTypes,
-                      [&](SubstTemplateTypeParmPackType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.SubstTemplateTypeParmPackTypes.size() ==
-           CP.SubstTemplateTypeParmPackTypesSize);
-  }
-
-  if (Ctx.SubstBuiltinTemplatePackTypes.size() !=
-      CP.SubstBuiltinTemplatePackTypesSize) {
-    llvm::dbgs() << "Ctx.SubstBuiltinTemplatePackTypes.size() != "
-                    "CP.SubstBuiltinTemplatePackTypesSize\n";
-    eraseFoldingSetIf(Ctx.SubstBuiltinTemplatePackTypes,
-                      [&](SubstBuiltinTemplatePackType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.SubstBuiltinTemplatePackTypes.size() ==
-           CP.SubstBuiltinTemplatePackTypesSize);
-  }
-
-  if (Ctx.TemplateSpecializationTypes.size() !=
-      CP.TemplateSpecializationTypesSize) {
-    llvm::dbgs() << "Ctx.TemplateSpecializationTypes.size() != "
-                    "CP.TemplateSpecializationTypesSize\n";
-    eraseFoldingSetIf(Ctx.TemplateSpecializationTypes,
-                      [&](TemplateSpecializationType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.TemplateSpecializationTypes.size() ==
-           CP.TemplateSpecializationTypesSize);
-  }
-
-  if (Ctx.ParenTypes.size() != CP.ParenTypesSize) {
-    llvm::dbgs() << "Ctx.ParenTypes.size() != CP.ParenTypesSize\n";
-    eraseFoldingSetIf(Ctx.ParenTypes, [&](ParenType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.ParenTypes.size() == CP.ParenTypesSize);
-  }
-
-  if (Ctx.TagTypes.size() != CP.TagTypesSize) {
-    llvm::dbgs() << "Ctx.TagTypes.size() != CP.TagTypesSize\n";
-    eraseFoldingSetIf(Ctx.TagTypes,
-                      [&](TagTypeFoldingSetPlaceholder &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.TagTypes.size() == CP.TagTypesSize);
-  }
-
-  if (Ctx.UnresolvedUsingTypes.size() != CP.UnresolvedUsingTypesSize) {
-    llvm::dbgs()
-        << "Ctx.UnresolvedUsingTypes.size() != CP.UnresolvedUsingTypesSize\n";
-    eraseFoldingSetIf(
-        Ctx.UnresolvedUsingTypes,
-        [&](FoldingSetPlaceholder<UnresolvedUsingType> &Node) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-              static_cast<void *>(&Node), SlabCP);
-        });
-    assert(Ctx.UnresolvedUsingTypes.size() == CP.UnresolvedUsingTypesSize);
-  }
-
-  if (Ctx.UsingTypes.size() != CP.UsingTypesSize) {
-    llvm::dbgs() << "Ctx.UsingTypes.size() != CP.UsingTypesSize\n";
-    eraseFoldingSetIf(Ctx.UsingTypes, [&](UsingType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.UsingTypes.size() == CP.UsingTypesSize);
-  }
-
-  if (Ctx.TypedefTypes.size() != CP.TypedefTypesSize) {
-    llvm::dbgs() << "Ctx.TypedefTypes.size() != CP.TypedefTypesSize\n";
-    eraseFoldingSetIf(Ctx.TypedefTypes,
-                      [&](FoldingSetPlaceholder<TypedefType> &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.TypedefTypes.size() == CP.TypedefTypesSize);
-  }
-
-  if (Ctx.DependentNameTypes.size() != CP.DependentNameTypesSize) {
-    llvm::dbgs()
-        << "Ctx.DependentNameTypes.size() != CP.DependentNameTypesSize\n";
-    eraseFoldingSetIf(Ctx.DependentNameTypes,
-                      [&](DependentNameType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentNameTypes.size() == CP.DependentNameTypesSize);
-  }
-
-  if (Ctx.PackExpansionTypes.size() != CP.PackExpansionTypesSize) {
-    llvm::dbgs()
-        << "Ctx.PackExpansionTypes.size() != CP.PackExpansionTypesSize\n";
-    eraseFoldingSetIf(Ctx.PackExpansionTypes,
-                      [&](PackExpansionType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.PackExpansionTypes.size() == CP.PackExpansionTypesSize);
-  }
-
-  // mutable llvm::FoldingSet<ObjCObjectTypeImpl> ObjCObjectTypes;
-  // mutable llvm::FoldingSet<ObjCObjectPointerType> ObjCObjectPointerTypes;
-
-  if (Ctx.UnaryTransformTypes.size() != CP.UnaryTransformTypesSize) {
-    llvm::dbgs()
-        << "Ctx.UnaryTransformTypes.size() != CP.UnaryTransformTypesSize\n";
-    eraseFoldingSetIf(Ctx.UnaryTransformTypes,
-                      [&](UnaryTransformType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.UnaryTransformTypes.size() == CP.UnaryTransformTypesSize);
-  }
-
-  if (Ctx.AutoTypes.size() != CP.AutoTypesSize) {
-    llvm::dbgs() << "Ctx.AutoTypes.size() != CP.AutoTypesSize\n";
-    // mutable llvm::DenseMap<llvm::FoldingSetNodeID, AutoType *> AutoTypes;
-    eraseDenseMapIf(
-        Ctx.AutoTypes,
-        [&](llvm::detail::DenseMapPair<llvm::FoldingSetNodeIDRef, AutoType *>
-                &KV) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-              static_cast<void *>(KV.getSecond()), SlabCP);
-        });
-    assert(Ctx.AutoTypes.size() == CP.AutoTypesSize);
-  }
-
-  if (Ctx.DeducedTemplateSpecializationTypes.size() !=
-      CP.DeducedTemplateSpecializationTypesSize) {
-    llvm::dbgs() << "Ctx.DeducedTemplateSpecializationTypes.size() != "
-                    "CP.DeducedTemplateSpecializationTypesSize\n";
-    eraseFoldingSetIf(Ctx.DeducedTemplateSpecializationTypes,
-                      [&](DeducedTemplateSpecializationType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DeducedTemplateSpecializationTypes.size() ==
-           CP.DeducedTemplateSpecializationTypesSize);
-  }
-
-  if (Ctx.AtomicTypes.size() != CP.AtomicTypesSize) {
-    llvm::dbgs() << "Ctx.AtomicTypes.size() != CP.AtomicTypesSize\n";
-    eraseFoldingSetIf(Ctx.AtomicTypes, [&](AtomicType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.AtomicTypes.size() == CP.AtomicTypesSize);
-  }
-
-  if (Ctx.AttributedTypes.size() != CP.AttributedTypesSize) {
-    llvm::dbgs() << "Ctx.AttributedTypes.size() != CP.AttributedTypesSize\n";
-    eraseFoldingSetIf(Ctx.AttributedTypes, [&](AttributedType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.AttributedTypes.size() == CP.AttributedTypesSize);
-  }
-
-  if (Ctx.PipeTypes.size() != CP.PipeTypesSize) {
-    llvm::dbgs() << "Ctx.PipeTypes.size() != CP.PipeTypesSize\n";
-    eraseFoldingSetIf(Ctx.PipeTypes, [&](PipeType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.PipeTypes.size() == CP.PipeTypesSize);
-  }
-
-  if (Ctx.BitIntTypes.size() != CP.BitIntTypesSize) {
-    llvm::dbgs() << "Ctx.BitIntTypes.size() != CP.BitIntTypesSize\n";
-    eraseFoldingSetIf(Ctx.BitIntTypes, [&](BitIntType &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(Ctx.BitIntTypes.size() == CP.BitIntTypesSize);
-  }
-
-  if (Ctx.DependentBitIntTypes.size() != CP.DependentBitIntTypesSize) {
-    llvm::dbgs()
-        << "Ctx.DependentBitIntTypes.size() != CP.DependentBitIntTypesSize\n";
-    eraseFoldingSetIf(Ctx.DependentBitIntTypes,
-                      [&](DependentBitIntType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentBitIntTypes.size() == CP.DependentBitIntTypesSize);
-  }
-
-  if (Ctx.BTFTagAttributedTypes.size() != CP.BTFTagAttributedTypesSize) {
-    llvm::dbgs() << "Ctx.BTFTagAttributedTypes.size() != "
-                    "CP.BTFTagAttributedTypesSize\n";
-    eraseFoldingSetIf(Ctx.BTFTagAttributedTypes,
-                      [&](BTFTagAttributedType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.BTFTagAttributedTypes.size() == CP.BTFTagAttributedTypesSize);
-  }
-
-  if (Ctx.HLSLAttributedResourceTypes.size() !=
-      CP.HLSLAttributedResourceTypesSize) {
-    llvm::dbgs() << "Ctx.HLSLAttributedResourceTypes.size() != "
-                    "CP.HLSLAttributedResourceTypesSize\n";
-    eraseFoldingSetIf(Ctx.HLSLAttributedResourceTypes,
-                      [&](HLSLAttributedResourceType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.HLSLAttributedResourceTypes.size() ==
-           CP.HLSLAttributedResourceTypesSize);
-  }
-
-  if (Ctx.HLSLInlineSpirvTypes.size() != CP.HLSLInlineSpirvTypesSize) {
-    llvm::dbgs()
-        << "Ctx.HLSLInlineSpirvTypes.size() != CP.HLSLInlineSpirvTypesSize\n";
-    eraseFoldingSetIf(Ctx.HLSLInlineSpirvTypes,
-                      [&](HLSLInlineSpirvType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.HLSLInlineSpirvTypes.size() == CP.HLSLInlineSpirvTypesSize);
-  }
-
-  if (Ctx.CountAttributedTypes.size() != CP.CountAttributedTypesSize) {
-    llvm::dbgs()
-        << "Ctx.CountAttributedTypes.size() != CP.CountAttributedTypesSize\n";
-    eraseFoldingSetIf(Ctx.CountAttributedTypes,
-                      [&](CountAttributedType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.CountAttributedTypes.size() == CP.CountAttributedTypesSize);
-  }
-
-  if (Ctx.QualifiedTemplateNames.size() != CP.QualifiedTemplateNamesSize) {
-    llvm::dbgs() << "Ctx.QualifiedTemplateNames.size() != "
-                    "CP.QualifiedTemplateNamesSize\n";
-    eraseFoldingSetIf(Ctx.QualifiedTemplateNames,
-                      [&](QualifiedTemplateName &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.QualifiedTemplateNames.size() == CP.QualifiedTemplateNamesSize);
-  }
-
-  if (Ctx.DependentTemplateNames.size() != CP.DependentTemplateNamesSize) {
-    llvm::dbgs() << "Ctx.DependentTemplateNames.size() != "
-                    "CP.DependentTemplateNamesSize\n";
-    eraseFoldingSetIf(Ctx.DependentTemplateNames,
-                      [&](DependentTemplateName &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DependentTemplateNames.size() == CP.DependentTemplateNamesSize);
-  }
-
-  if (Ctx.SubstTemplateTemplateParms.size() !=
-      CP.SubstTemplateTemplateParmsSize) {
-    llvm::dbgs() << "Ctx.SubstTemplateTemplateParms.size() != "
-                    "CP.SubstTemplateTemplateParmsSize\n";
-    eraseFoldingSetIf(Ctx.SubstTemplateTemplateParms,
-                      [&](SubstTemplateTemplateParmStorage &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.SubstTemplateTemplateParms.size() ==
-           CP.SubstTemplateTemplateParmsSize);
-  }
-
-  if (Ctx.SubstTemplateTemplateParmPacks.size() !=
-      CP.SubstTemplateTemplateParmPacksSize) {
-    llvm::dbgs() << "Ctx.SubstTemplateTemplateParmPacks.size() != "
-                    "CP.SubstTemplateTemplateParmPacksSize\n";
-    eraseFoldingSetIf(Ctx.SubstTemplateTemplateParmPacks,
-                      [&](SubstTemplateTemplateParmPackStorage &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.SubstTemplateTemplateParmPacks.size() ==
-           CP.SubstTemplateTemplateParmPacksSize);
-  }
-
-  if (Ctx.DeducedTemplates.size() != CP.DeducedTemplatesSize) {
-    llvm::dbgs() << "Ctx.DeducedTemplates.size() != CP.DeducedTemplatesSize\n";
-    eraseFoldingSetIf(Ctx.DeducedTemplates,
-                      [&](DeducedTemplateStorage &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.DeducedTemplates.size() == CP.DeducedTemplatesSize);
-  }
-
-  if (Ctx.ArrayParameterTypes.size() != CP.ArrayParameterTypesSize) {
-    llvm::dbgs()
-        << "Ctx.ArrayParameterTypes.size() != CP.ArrayParameterTypesSize\n";
-    eraseFoldingSetIf(Ctx.ArrayParameterTypes,
-                      [&](ArrayParameterType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(Ctx.ArrayParameterTypes.size() == CP.ArrayParameterTypesSize);
-  }
-
-  if (CP.PredefinedSugarTypesSize != Ctx.PredefinedSugarTypes.size()) {
-    llvm::dbgs() << "if (CP.PredefinedSugarTypesSize != "
-                    "Ctx.PredefinedSugarTypes.size()\n";
-    //                 mutable std::array<Type *,
-    //                llvm::to_underlying(PredefinedSugarType::Kind::Last) +
-    //                1>
-    // PredefinedSugarTypes{};
-    assert(CP.PredefinedSugarTypesSize == Ctx.PredefinedSugarTypes.size());
-  }
-
-  if (CP.NamespaceAndPrefixStoragesSize !=
-      Ctx.NamespaceAndPrefixStorages.size()) {
-    llvm::dbgs() << "if (CP.NamespaceAndPrefixStoragesSize != "
-                    "Ctx.NamespaceAndPrefixStorages.size()\n";
-    eraseFoldingSetIf(Ctx.NamespaceAndPrefixStorages,
-                      [&](NamespaceAndPrefixStorage &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(CP.NamespaceAndPrefixStoragesSize ==
-           Ctx.NamespaceAndPrefixStorages.size());
-  }
-
-  if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()) {
-    llvm::dbgs()
-        << "if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()\n";
-    std::vector<const ASTRecordLayout *> ToBeDestroyed;
-    eraseDenseMapIf(
-        Ctx.ASTRecordLayouts,
-        [&](llvm::detail::DenseMapPair<const RecordDecl *,
-                                       const ASTRecordLayout *> &KV) -> bool {
-          bool IsAfter =
-              Ctx.getAllocator().isAfterCheckpoint(
-                  static_cast<void *>(const_cast<RecordDecl *>(KV.getFirst())),
-                  SlabCP) ||
-              Ctx.getAllocator().isAfterCheckpoint(
-                  static_cast<void *>(
-                      const_cast<ASTRecordLayout *>(KV.getSecond())),
-                  SlabCP);
-          if (IsAfter)
-            const_cast<ASTRecordLayout *>(KV.getSecond())->Destroy(Ctx);
-          return IsAfter;
-        });
-    assert(CP.ASTRecordLayoutsSize == Ctx.ASTRecordLayouts.size());
-  }
-
-  if (CP.MemoizedTypeInfoSize != Ctx.MemoizedTypeInfo.size()) {
-    llvm::dbgs()
-        << "if (CP.MemoizedTypeInfoSize != Ctx.MemoizedTypeInfo.size() "
-        << CP.MemoizedTypeInfoSize << " != " << Ctx.MemoizedTypeInfo.size()
-        << " \n";
-    eraseDenseMapIf(
-        Ctx.MemoizedTypeInfo,
-        [&](llvm::detail::DenseMapPair<const Type *, struct TypeInfo> &KV)
-            -> bool {
-          // llvm::outs() << "Type * In Range : [ "
-          //              << static_cast<void *>(SlabCP.CurPtr) << " < "
-          //              << KV.getFirst() << " < "
-          //              << static_cast<void *>(SlabCP.End) << "]\n";
-          return Ctx.getAllocator().isAfterCheckpoint(
-              static_cast<void *>(const_cast<Type *>(KV.getFirst())), SlabCP);
-        });
-    // assert(CP.MemoizedTypeInfoSize == Ctx.MemoizedTypeInfo.size());
-  }
-
-  if (CP.MemoizedUnadjustedAlignSize != Ctx.MemoizedUnadjustedAlign.size()) {
-    llvm::dbgs() << "if (CP.MemoizedUnadjustedAlignSize != "
-                    "Ctx.MemoizedUnadjustedAlign.size()\n";
-    eraseDenseMapIf(
-        Ctx.MemoizedUnadjustedAlign,
-        [&](llvm::detail::DenseMapPair<const Type *, unsigned> &KV) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-              static_cast<void *>(const_cast<Type *>(KV.getFirst())), SlabCP);
-        });
-    assert(CP.MemoizedUnadjustedAlignSize ==
-           Ctx.MemoizedUnadjustedAlign.size());
-  }
-
-  if (CP.KeyFunctionsSize != Ctx.KeyFunctions.size()) {
-    llvm::dbgs() << "if (CP.KeyFunctionsSize != Ctx.KeyFunctions.size()\n";
-    eraseDenseMapIf(
-        Ctx.KeyFunctions,
-        [&](llvm::detail::DenseMapPair<const CXXRecordDecl *, LazyDeclPtr> &KV)
-            -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-              static_cast<void *>(const_cast<CXXRecordDecl *>(KV.getFirst())),
-              SlabCP);
-        });
-    // assert(CP.KeyFunctionsSize == Ctx.KeyFunctions.size());
-  }
-
-  if (CP.BlockVarCopyInitsSize != Ctx.BlockVarCopyInits.size()) {
-    llvm::dbgs()
-        << "if (CP.BlockVarCopyInitsSize != Ctx.BlockVarCopyInits.size()\n";
-    eraseDenseMapIf(
-        Ctx.BlockVarCopyInits,
-        [&](llvm::detail::DenseMapPair<const VarDecl *, BlockVarCopyInit> &KV)
-            -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-              static_cast<void *>(const_cast<VarDecl *>(KV.getFirst())),
-              SlabCP);
-        });
-    assert(CP.BlockVarCopyInitsSize == Ctx.BlockVarCopyInits.size());
-  }
-
-  if (CP.MSGuidDeclsSize != Ctx.MSGuidDecls.size()) {
-    llvm::dbgs() << "if (CP.MSGuidDeclsSize != Ctx.MSGuidDecls.size()\n";
-    eraseFoldingSetIf(Ctx.MSGuidDecls, [&](MSGuidDecl &Node) -> bool {
-      return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
-                                                  SlabCP);
-    });
-    assert(CP.MSGuidDeclsSize == Ctx.MSGuidDecls.size());
-  }
-
-  if (CP.UnnamedGlobalConstantDeclsSize !=
-      Ctx.UnnamedGlobalConstantDecls.size()) {
-    llvm::dbgs() << "if (CP.UnnamedGlobalConstantDeclsSize != "
-                    "Ctx.UnnamedGlobalConstantDecls.size()\n";
-    eraseFoldingSetIf(Ctx.UnnamedGlobalConstantDecls,
-                      [&](UnnamedGlobalConstantDecl &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(CP.UnnamedGlobalConstantDeclsSize ==
-           Ctx.UnnamedGlobalConstantDecls.size());
-  }
-
-  if (CP.TemplateParamObjectDeclsSize != Ctx.TemplateParamObjectDecls.size()) {
-    llvm::dbgs() << "if (CP.TemplateParamObjectDeclsSize != "
-                    "Ctx.TemplateParamObjectDecls.size()\n";
-    eraseFoldingSetIf(Ctx.TemplateParamObjectDecls,
-                      [&](TemplateParamObjectDecl &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(CP.TemplateParamObjectDeclsSize ==
-           Ctx.TemplateParamObjectDecls.size());
-  }
-
-  if (CP.StringLiteralCacheSize != Ctx.StringLiteralCache.size()) {
-    llvm::dbgs()
-        << "if (CP.StringLiteralCacheSize != Ctx.StringLiteralCache.size()\n";
-    //   mutable llvm::StringMap<StringLiteral *> StringLiteralCache;
-    assert(CP.StringLiteralCacheSize == Ctx.StringLiteralCache.size());
-  }
-
-  if (CP.DestroyingOperatorDeletesSize !=
-      Ctx.DestroyingOperatorDeletes.size()) {
-    llvm::dbgs() << "if (CP.DestroyingOperatorDeletesSize != "
-                    "Ctx.DestroyingOperatorDeletes.size()\n";
-    eraseDenseSetIf(
-        Ctx.DestroyingOperatorDeletes, [&](const FunctionDecl *FD) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-              static_cast<void *>(const_cast<FunctionDecl *>(FD)), SlabCP);
-        });
-    assert(CP.DestroyingOperatorDeletesSize ==
-           Ctx.DestroyingOperatorDeletes.size());
-  }
-
-  if (CP.TypeAwareOperatorNewAndDeletesSize !=
-      Ctx.TypeAwareOperatorNewAndDeletes.size()) {
-    llvm::dbgs() << "CP.TypeAwareOperatorNewAndDeletesSize != "
-                    "Ctx.TypeAwareOperatorNewAndDeletes.size()\n";
-    eraseDenseSetIf(Ctx.TypeAwareOperatorNewAndDeletes,
-                    [&](const FunctionDecl *FD) -> bool {
-                      return Ctx.getAllocator().isAfterCheckpoint(
-                          static_cast<void *>(const_cast<FunctionDecl *>(FD)),
-                          SlabCP);
-                    });
-    assert(CP.TypeAwareOperatorNewAndDeletesSize ==
-           Ctx.TypeAwareOperatorNewAndDeletes.size());
-  }
-
-  if (CP.OperatorDeletesForVirtualDtorSize !=
-      Ctx.OperatorDeletesForVirtualDtor.size()) {
-    llvm::dbgs() << "CP.OperatorDeletesForVirtualDtorSize != "
-                    "Ctx.OperatorDeletesForVirtualDtor.size()\n";
-    eraseDenseMapIf(
-        Ctx.OperatorDeletesForVirtualDtor,
-        [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
-                                       FunctionDecl *> &KV) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-                     static_cast<void *>(
-                         const_cast<CXXDestructorDecl *>(KV.getFirst())),
-                     SlabCP) ||
-                 Ctx.getAllocator().isAfterCheckpoint(
-                     static_cast<void *>(KV.getSecond()), SlabCP);
-        });
-    // assert(CP.OperatorDeletesForVirtualDtorSize ==
-    //        Ctx.OperatorDeletesForVirtualDtor.size());
-  }
-
-  if (CP.GlobalOperatorDeletesForVirtualDtorSize !=
-      Ctx.GlobalOperatorDeletesForVirtualDtor.size()) {
-    llvm::dbgs() << "CP.GlobalOperatorDeletesForVirtualDtorSize != "
-                    "Ctx.GlobalOperatorDeletesForVirtualDtor.size()\n";
-    eraseDenseMapIf(
-        Ctx.GlobalOperatorDeletesForVirtualDtor,
-        [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
-                                       FunctionDecl *> &KV) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-                     static_cast<void *>(
-                         const_cast<CXXDestructorDecl *>(KV.getFirst())),
-                     SlabCP) ||
-                 Ctx.getAllocator().isAfterCheckpoint(
-                     static_cast<void *>(KV.getSecond()), SlabCP);
-        });
-    assert(CP.GlobalOperatorDeletesForVirtualDtorSize ==
-           Ctx.GlobalOperatorDeletesForVirtualDtor.size());
-  }
-
-  if (CP.ArrayOperatorDeletesForVirtualDtorSize !=
-      Ctx.ArrayOperatorDeletesForVirtualDtor.size()) {
-    llvm::dbgs() << "CP.ArrayOperatorDeletesForVirtualDtorSize != "
-                    "Ctx.ArrayOperatorDeletesForVirtualDtor.size()\n";
-    eraseDenseMapIf(
-        Ctx.ArrayOperatorDeletesForVirtualDtor,
-        [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
-                                       FunctionDecl *> &KV) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-                     static_cast<void *>(
-                         const_cast<CXXDestructorDecl *>(KV.getFirst())),
-                     SlabCP) ||
-                 Ctx.getAllocator().isAfterCheckpoint(
-                     static_cast<void *>(KV.getSecond()), SlabCP);
-        });
-    assert(CP.ArrayOperatorDeletesForVirtualDtorSize ==
-           Ctx.ArrayOperatorDeletesForVirtualDtor.size());
-  }
-
-  if (CP.GlobalArrayOperatorDeletesForVirtualDtorSize !=
-      Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size()) {
-    llvm::dbgs() << "CP.GlobalArrayOperatorDeletesForVirtualDtorSize != "
-                    "Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size()\n";
-    eraseDenseMapIf(
-        Ctx.GlobalArrayOperatorDeletesForVirtualDtor,
-        [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
-                                       FunctionDecl *> &KV) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-                     static_cast<void *>(
-                         const_cast<CXXDestructorDecl *>(KV.getFirst())),
-                     SlabCP) ||
-                 Ctx.getAllocator().isAfterCheckpoint(
-                     static_cast<void *>(KV.getSecond()), SlabCP);
-        });
-    assert(CP.GlobalArrayOperatorDeletesForVirtualDtorSize ==
-           Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size());
-  }
-
-  if (CP.MaybeRequireVectorDeletingDtorSize !=
-      Ctx.MaybeRequireVectorDeletingDtor.size()) {
-    llvm::dbgs() << "if (CP.MaybeRequireVectorDeletingDtorSize != "
-                    "Ctx.MaybeRequireVectorDeletingDtor.size()\n";
-    eraseDenseSetIf(Ctx.MaybeRequireVectorDeletingDtor,
-                    [&](const CXXRecordDecl *RD) -> bool {
-                      return Ctx.getAllocator().isAfterCheckpoint(
-                          static_cast<void *>(const_cast<CXXRecordDecl *>(RD)),
-                          SlabCP);
-                    });
-    assert(CP.MaybeRequireVectorDeletingDtorSize ==
-           Ctx.MaybeRequireVectorDeletingDtor.size());
-  }
-
-  if (CP.MergedDeclsSize != Ctx.MergedDecls.size()) {
-    llvm::dbgs() << "CP.MergedDeclsSize != Ctx.MergedDecls.size()\n";
-    assert(CP.MergedDeclsSize == Ctx.MergedDecls.size());
-  }
-
-  if (CP.DeclAttrsSize != Ctx.DeclAttrs.size()) {
-    llvm::dbgs() << "CP.DeclAttrsSize != Ctx.DeclAttrs.size()\n";
-    eraseDenseMapIf(
-        Ctx.DeclAttrs,
-        [&](llvm::detail::DenseMapPair<const Decl *, AttrVec *> &KV) -> bool {
-          bool isAfter =
-              Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP) ||
-              Ctx.getAllocator().isAfterCheckpoint(KV.getSecond(), SlabCP);
-          if (isAfter)
-            KV.getSecond()->~AttrVec();
-          return isAfter;
-        });
-    assert(CP.DeclAttrsSize == Ctx.DeclAttrs.size());
-  }
-
-  if (CP.MergedDefModulesSize != Ctx.MergedDefModules.size()) {
-    llvm::dbgs() << "CP.MergedDefModulesSize != Ctx.MergedDefModules.size()\n";
-    assert(CP.MergedDefModulesSize == Ctx.MergedDefModules.size());
-  }
-
-  if (CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()) {
-    llvm::dbgs()
-        << "CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()\n";
-    assert(CP.ModuleInitializersSize == Ctx.ModuleInitializers.size());
-  }
-
-  if (CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()) {
-    llvm::dbgs()
-        << "CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()\n";
-    assert(CP.PrimaryModuleNameMapSize == Ctx.PrimaryModuleNameMap.size());
-  }
-
-  if (CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()) {
-    llvm::dbgs()
-        << "CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()\n";
-    assert(CP.SameModuleLookupSetSize == Ctx.SameModuleLookupSet.size());
-  }
-
-  if (CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()) {
-    llvm::dbgs() << "CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()\n";
-    assert(CP.ScalableVecTyMapSize == Ctx.ScalableVecTyMap.size());
-  }
-
-  if (CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()) {
-    llvm::dbgs() << "CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()\n";
-    assert(CP.LambdaCastPathsSize == Ctx.LambdaCastPaths.size());
-  }
-
-  if (CP.RawCommentsSize != Ctx.RawComments.size()) {
-    llvm::dbgs() << "CP.RawCommentsSize != Ctx.RawComments.size()\n";
-    assert(CP.RawCommentsSize == Ctx.RawComments.size());
-  }
-
-  if (CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()) {
-    llvm::dbgs()
-        << "CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()\n";
-    assert(CP.RedeclChainCommentsSize == Ctx.RedeclChainComments.size());
-  }
-
-  if (CP.CommentlessRedeclChainsSize != Ctx.CommentlessRedeclChains.size()) {
-    llvm::dbgs() << "CP.CommentlessRedeclChainsSize != "
-                    "Ctx.CommentlessRedeclChains.size()\n";
-    assert(CP.CommentlessRedeclChainsSize ==
-           Ctx.CommentlessRedeclChains.size());
-  }
-
-  if (CP.ParsedCommentsSize != Ctx.ParsedComments.size()) {
-    llvm::dbgs() << "CP.ParsedCommentsSize != Ctx.ParsedComments.size()\n";
-    assert(CP.ParsedCommentsSize == Ctx.ParsedComments.size());
-  }
-
-  if (CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()) {
-    llvm::dbgs()
-        << "CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()\n";
-    assert(CP.RelocatableClassesSize == Ctx.RelocatableClasses.size());
-  }
-
-  if (CP.ParamIndicesSize != Ctx.ParamIndices.size()) {
-    llvm::dbgs() << "CP.ParamIndicesSize != Ctx.ParamIndices.size()\n";
-    assert(CP.ParamIndicesSize == Ctx.ParamIndices.size());
-  }
-
-  if (CP.MangleNumbersSize != Ctx.MangleNumbers.size()) {
-    llvm::dbgs() << "CP.MangleNumbersSize != Ctx.MangleNumbers.size()\n";
-    assert(CP.MangleNumbersSize == Ctx.MangleNumbers.size());
-  }
-
-  if (CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()) {
-    llvm::dbgs()
-        << "CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()\n";
-    assert(CP.StaticLocalNumbersSize == Ctx.StaticLocalNumbers.size());
-  }
-
-  if (CP.TemplateOrInstantiationSize != Ctx.TemplateOrInstantiation.size()) {
-    llvm::dbgs() << "CP.TemplateOrInstantiationSize != "
-                    "Ctx.TemplateOrInstantiation.size()\n";
-    //                     llvm::DenseMap<const VarDecl *,
-    //                     TemplateOrSpecializationInfo>
-    // TemplateOrInstantiation;
-    eraseDenseMapIf(
-        Ctx.TemplateOrInstantiation,
-        [&](llvm::detail::DenseMapPair<
-            const VarDecl *, ASTContext::TemplateOrSpecializationInfo> &KV)
-            -> bool {
-          ASTContext::TemplateOrSpecializationInfo &T = KV.getSecond();
-          bool IsAfterCP = false;
-          if (auto *M = T.dyn_cast<MemberSpecializationInfo *>())
-            IsAfterCP = Ctx.getAllocator().isAfterCheckpoint(M, SlabCP);
-          if (auto *V = T.dyn_cast<VarTemplateDecl *>())
-            IsAfterCP = Ctx.getAllocator().isAfterCheckpoint(V, SlabCP);
-          return IsAfterCP ||
-                 Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
-        });
-    assert(CP.TemplateOrInstantiationSize ==
-           Ctx.TemplateOrInstantiation.size());
-  }
-
-  if (CP.InstantiatedFromUsingDeclSize !=
-      Ctx.InstantiatedFromUsingDecl.size()) {
-    llvm::dbgs() << "CP.InstantiatedFromUsingDeclSize != "
-                    "Ctx.InstantiatedFromUsingDecl.size()\n";
-    assert(CP.InstantiatedFromUsingDeclSize ==
-           Ctx.InstantiatedFromUsingDecl.size());
-  }
-
-  if (CP.InstantiatedFromUsingEnumDeclSize !=
-      Ctx.InstantiatedFromUsingEnumDecl.size()) {
-    llvm::dbgs() << "CP.InstantiatedFromUsingEnumDeclSize != "
-                    "Ctx.InstantiatedFromUsingEnumDecl.size()\n";
-    assert(CP.InstantiatedFromUsingEnumDeclSize ==
-           Ctx.InstantiatedFromUsingEnumDecl.size());
-  }
-
-  if (CP.InstantiatedFromUsingShadowDeclSize !=
-      Ctx.InstantiatedFromUsingShadowDecl.size()) {
-    llvm::dbgs() << "CP.InstantiatedFromUsingShadowDeclSize != "
-                    "Ctx.InstantiatedFromUsingShadowDecl.size()\n";
-    assert(CP.InstantiatedFromUsingShadowDeclSize ==
-           Ctx.InstantiatedFromUsingShadowDecl.size());
-  }
-
-  if (CP.InstantiatedFromUnnamedFieldDeclSize !=
-      Ctx.InstantiatedFromUnnamedFieldDecl.size()) {
-    llvm::dbgs() << "CP.InstantiatedFromUnnamedFieldDeclSize != "
-                    "Ctx.InstantiatedFromUnnamedFieldDecl.size()\n";
-    assert(CP.InstantiatedFromUnnamedFieldDeclSize ==
-           Ctx.InstantiatedFromUnnamedFieldDecl.size());
-  }
-
-  if (CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()) {
-    llvm::dbgs()
-        << "CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()\n";
-    // using CXXMethodVector = llvm::TinyPtrVector<const CXXMethodDecl *>;
-    // llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector> OverriddenMethods;
-    // eraseDenseMapIf(
-    //     Ctx.OverriddenMethods,
-    //     [&](llvm::detail::DenseMapPair<const CXXMethodDecl *,
-    //     CXXMethodVector>
-    //             &KV) -> bool {
-    //       bool isAfter = Ctx.getAllocater().isAfterCheckpoint(
-    //           static_cast<void *>(const_cast<CXXMethodDecl
-    //           *>(KV.getFirst())), SlabCP);
-    //       if (!isAfter) {
-    //         std::vector<CXXMethodDecl *> ToBeRemoved;
-    //         ToBeRemoved.reserve(KV.getSecond().size());
-    //         for (auto *M : std::reverse(KV.getSecond()))
-    //           if (Ctx.getAllocater().isAfterCheckpoint(
-    //                   static_cast<void *>(const_cast<CXXMethodDecl *>(M)),
-    //                   SlabCP)) {
-    //             ToBeRemoved.push_back(M);
-    //           }
-    //         for (auto *M : ToBeRemoved)
-    //           KV.getSecond().erase(M);
-    //       }
-    //       return isAfter;
-    //     });
-
-    for (auto &KV : Ctx.OverriddenMethods) {
-      if (Ctx.getAllocator().isAfterCheckpoint(KV.first, SlabCP))
-        continue; // entire entry will be removed in second pass
-
-      auto &Vec = KV.getSecond();
-      llvm::SmallVector<const CXXMethodDecl *, 4> ToRemove;
-      for (auto *M : Vec)
-        if (Ctx.getAllocator().isAfterCheckpoint(M, SlabCP))
-          ToRemove.push_back(M);
-
-      for (auto *M : ToRemove)
-        Vec.erase(llvm::find(Vec, M));
-    }
-
-    eraseDenseMapIf(
-        Ctx.OverriddenMethods,
-        [&](llvm::detail::DenseMapPair<
-            const CXXMethodDecl *, ASTContext::CXXMethodVector> &KV) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
-        });
-    assert(CP.OverriddenMethodsSize == Ctx.OverriddenMethods.size());
-  }
-
-  if (CP.MangleNumberingContextsSize != Ctx.MangleNumberingContexts.size()) {
-    llvm::dbgs() << "CP.MangleNumberingContextsSize != "
-                    "Ctx.MangleNumberingContexts.size()\n";
-    eraseDenseMapIf(
-        Ctx.MangleNumberingContexts,
-        [&](llvm::detail::DenseMapPair<
-            const DeclContext *, std::unique_ptr<MangleNumberingContext>> &KV)
-            -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
-        });
-    assert(CP.MangleNumberingContextsSize ==
-           Ctx.MangleNumberingContexts.size());
-  }
-
-  if (CP.ExtraMangleNumberingContextsSize !=
-      Ctx.ExtraMangleNumberingContexts.size()) {
-    llvm::dbgs() << "CP.ExtraMangleNumberingContextsSize != "
-                    "Ctx.ExtraMangleNumberingContexts.size()\n";
-    assert(CP.ExtraMangleNumberingContextsSize ==
-           Ctx.ExtraMangleNumberingContexts.size());
-  }
-
-  if (CP.TraversalScopeSize != Ctx.TraversalScope.size()) {
-    llvm::dbgs() << "CP.TraversalScopeSize != "
-                    "Ctx.TraversalScope.size()\n";
-    assert(CP.TraversalScopeSize == Ctx.TraversalScope.size());
-  }
-
-  /// Obj-C
-  if (CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()) {
-    llvm::dbgs() << "CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()\n";
-    eraseFoldingSetIf(Ctx.ObjCObjectTypes,
-                      [&](ObjCObjectTypeImpl &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(CP.ObjCObjectTypesSize == Ctx.ObjCObjectTypes.size());
-  }
-
-  if (CP.ObjCObjectPointerTypesSize != Ctx.ObjCObjectPointerTypes.size()) {
-    llvm::dbgs() << "CP.ObjCObjectPointerTypesSize != "
-                    "Ctx.ObjCObjectPointerTypes.size()\n";
-    eraseFoldingSetIf(Ctx.ObjCObjectPointerTypes,
-                      [&](ObjCObjectPointerType &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(
-                            static_cast<void *>(&Node), SlabCP);
-                      });
-    assert(CP.ObjCObjectPointerTypesSize == Ctx.ObjCObjectPointerTypes.size());
-  }
-
-  if (CP.ObjCIdDeclCP != Ctx.ObjCIdDecl) {
-    llvm::dbgs() << "CP.ObjCIdDeclCP != Ctx.ObjCIdDecl\n";
-    Ctx.ObjCIdDecl = CP.ObjCIdDeclCP;
-    assert(CP.ObjCIdDeclCP == Ctx.ObjCIdDecl);
-  }
-  if (CP.ObjCSelDeclCP != Ctx.ObjCSelDecl) {
-    llvm::dbgs() << "CP.ObjCSelDeclCP != Ctx.ObjCSelDecl\n";
-    Ctx.ObjCSelDecl = CP.ObjCSelDeclCP;
-    assert(CP.ObjCSelDeclCP == Ctx.ObjCSelDecl);
-  }
-  if (CP.ObjCClassDeclCP != Ctx.ObjCClassDecl) {
-    llvm::dbgs() << "CP.ObjCClassDeclCP != Ctx.ObjCClassDecl\n";
-    Ctx.ObjCClassDecl = CP.ObjCClassDeclCP;
-    assert(CP.ObjCClassDeclCP == Ctx.ObjCClassDecl);
-  }
-  if (CP.ObjCProtocolClassDeclCP != Ctx.ObjCProtocolClassDecl) {
-    llvm::dbgs() << "CP.ObjCProtocolClassDeclCP != Ctx.ObjCProtocolClassDecl\n";
-    Ctx.ObjCProtocolClassDecl = CP.ObjCProtocolClassDeclCP;
-    assert(CP.ObjCProtocolClassDeclCP == Ctx.ObjCProtocolClassDecl);
-  }
-
-  if (CP.DeclarationNames.CXXConstructorNamesSize !=
-      Ctx.DeclarationNames.CXXConstructorNames.size()) {
-    llvm::dbgs() << "CP.DeclarationNames.CXXConstructorNamesSize != "
-                    "Ctx.DeclarationNames.CXXConstructorNames.size()";
-    eraseFoldingSetIf(Ctx.DeclarationNames.CXXConstructorNames,
-                      [&](detail::CXXSpecialNameExtra &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(&Node,
-                                                                    SlabCP);
-                      });
-    assert(CP.DeclarationNames.CXXConstructorNamesSize ==
-           Ctx.DeclarationNames.CXXConstructorNames.size());
-  }
-  if (CP.DeclarationNames.CXXDestructorNamesSize !=
-      Ctx.DeclarationNames.CXXDestructorNames.size()) {
-    llvm::dbgs() << "CP.DeclarationNames.CXXDestructorNamesSize != "
-                    "Ctx.DeclarationNames.CXXDestructorNames.size()";
-    eraseFoldingSetIf(Ctx.DeclarationNames.CXXDestructorNames,
-                      [&](detail::CXXSpecialNameExtra &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(&Node,
-                                                                    SlabCP);
-                      });
-    assert(CP.DeclarationNames.CXXDestructorNamesSize ==
-           Ctx.DeclarationNames.CXXDestructorNames.size());
-  }
-  if (CP.DeclarationNames.CXXConversionFunctionNamesSize !=
-      Ctx.DeclarationNames.CXXConversionFunctionNames.size()) {
-    llvm::dbgs() << "CP.DeclarationNames.CXXConversionFunctionNamesSize != "
-                    "Ctx.DeclarationNames.CXXConversionFunctionNames.size()";
-    eraseFoldingSetIf(Ctx.DeclarationNames.CXXConversionFunctionNames,
-                      [&](detail::CXXSpecialNameExtra &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(&Node,
-                                                                    SlabCP);
-                      });
-    assert(CP.DeclarationNames.CXXConversionFunctionNamesSize ==
-           Ctx.DeclarationNames.CXXConversionFunctionNames.size());
-  }
-  if (CP.DeclarationNames.CXXLiteralOperatorNamesSize !=
-      Ctx.DeclarationNames.CXXLiteralOperatorNames.size()) {
-    llvm::dbgs() << "CP.DeclarationNames.CXXLiteralOperatorNamesSize != "
-                    "Ctx.DeclarationNames.CXXLiteralOperatorNames.size()";
-    eraseFoldingSetIf(Ctx.DeclarationNames.CXXLiteralOperatorNames,
-                      [&](detail::CXXLiteralOperatorIdName &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(&Node,
-                                                                    SlabCP);
-                      });
-    assert(CP.DeclarationNames.CXXLiteralOperatorNamesSize ==
-           Ctx.DeclarationNames.CXXLiteralOperatorNames.size());
-  }
-  if (CP.DeclarationNames.CXXDeductionGuideNamesSize !=
-      Ctx.DeclarationNames.CXXDeductionGuideNames.size()) {
-    llvm::dbgs() << "CP.DeclarationNames.CXXDeductionGuideNamesSize != "
-                    "Ctx.DeclarationNames.CXXDeductionGuideNames.size()";
-    eraseFoldingSetIf(Ctx.DeclarationNames.CXXDeductionGuideNames,
-                      [&](detail::CXXDeductionGuideNameExtra &Node) -> bool {
-                        return Ctx.getAllocator().isAfterCheckpoint(&Node,
-                                                                    SlabCP);
-                      });
-    assert(CP.DeclarationNames.CXXDeductionGuideNamesSize ==
-           Ctx.DeclarationNames.CXXDeductionGuideNames.size());
-  }
-  for (unsigned I = 0; I < NUM_OVERLOADED_OPERATORS; I++)
-    Ctx.DeclarationNames.CXXOperatorNames[I] =
-        CP.DeclarationNames.CXXOperatorNamesCP[I];
-
-  // for (auto &[Decl, OldTy] : Ctx.PendingTypeForDeclMutations)
-  //   Decl->TypeForDecl = OldTy;
-
-  // Ctx.PendingTypeForDeclMutations.clear();
-  // TranslationUnitDecl *MostRecentTU = Ctx.getTranslationUnitDecl();
-  // for (const auto *DC : Ctx.PendingDCMutations) {
-  //   // if (MostRecentTU->getPrimaryContext() == DC)
-  //   //   continue;
-  //   if (StoredDeclsMap *Map = const_cast<DeclContext *>(DC)
-  //                                 ->getPrimaryContext()
-  //                                 ->getLookupPtr()) {
-  //     for (auto &&[Key, List] : *Map) {
-  //       DeclContextLookupResult R = List.getLookupResult();
-  //       std::vector<NamedDecl *> NamedDeclsToRemove;
-  //       // bool RemoveAll = true;
-  //       for (NamedDecl *D : R) {
-  //         // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU ("
-  //         <<
-  //         // (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
-  //         // llvm::outs() << "DeclContext : " << DC << "\n";
-  //         // D->dump();
-  //         // if (D->getTranslationUnitDecl() == MostRecentTU)
-  //         if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D),
-  //                                                  SlabCP))
-  //           NamedDeclsToRemove.push_back(D);
-  //         // else
-  //         // RemoveAll = false;
-  //       }
-  //       // if (LLVM_LIKELY(RemoveAll)) {
-  //       //   Map->erase(Key);
-  //       // } else {
-  //       for (NamedDecl *D : NamedDeclsToRemove)
-  //         List.remove(D);
-  //       // }
-  //     }
-  //   }
-
-  // Decl *Prev = DC->FirstDecl;
-  // Decl *Cur = DC->FirstDecl;
-  // while (Cur) {
-  //   if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
-  //                                            SlabCP)) {
-  //     DC->LastDecl = Prev;
-  //     DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
-  //     break;
-  //   }
-  //   Prev = Cur;
-  //   Cur = Cur->getNextDeclInContext();
-  // }
-  // }
-
-  // if (FirstDecl) {
-  //   LastDecl->NextInContextAndBits.setPointer(D);
-  //   LastDecl = D;
-  // } else {
-  //   FirstDecl = LastDecl = D;
-  // }
-
-  // Notify a C++ record declaration that we've added a member, so it can
-  // update its class-specific state.
-  // if (auto *Record = dyn_cast<CXXRecordDecl>(this))
-  //   Record->addedMember(D);
-
-  //   ImportDecl *FirstLocalImport = nullptr;
-  // ImportDecl *LastLocalImport = nullptr;
-
-  // class ImportDecl final : public Decl,
-  //                        llvm::TrailingObjects<ImportDecl, SourceLocation> {
-  // friend class ASTContext;
-  // friend class ASTDeclReader;
-  // friend class ASTReader;
-  // friend TrailingObjects;
-
-  // /// The imported module.
-  // Module *ImportedModule = nullptr;
-
-  // /// The next import in the list of imports local to the translation
-  // /// unit being parsed (not loaded from an AST file).
-  // ///
-  // /// Includes a bit that indicates whether we have source-location
-  // information
-  // /// for each identifier in the module name.
-  // ///
-  // /// When the bit is false, we only have a single source location for the
-  // /// end of the import declaration.
-  // llvm::PointerIntPair<ImportDecl *, 1, bool> NextLocalImportAndComplete;
-
-  // llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM = Ctx.LastSDM;
-
-  // StoredDeclsMap *Map = LastSDM.getPointer();
-  // bool Dependent = LastSDM.getInt();
-  // while (Map && (Map != CP.LastSDM.getPointer())) {
-  //   // llvm::outs() << "we are deleting LASTSDM\n";
-  // //   // Advance the iteration before we invalidate memory.
-  //   llvm::PointerIntPair<StoredDeclsMap*,1> Next = Map->Previous;
-
-  //   if (Dependent)
-  //     delete static_cast<DependentStoredDeclsMap*>(Map);
-  //   else
-  //     delete Map;
-
-  //   Map = Next.getPointer();
-  //   Dependent = Next.getInt();
-  // }
-
-  // Ctx.LastSDM = CP.LastSDM;
-  DeclListNode *FreeNodeHead = Ctx.ListNodeFreeList;
-  while (FreeNodeHead) {
-    if (!Ctx.getAllocator().isAfterCheckpoint(FreeNodeHead, SlabCP))
-      break;
-    FreeNodeHead = dyn_cast_if_present<DeclListNode *>(FreeNodeHead->Rest);
-  }
-
-  Ctx.ListNodeFreeList = FreeNodeHead;
-
-  Ctx.Types.resize(CP.TypesSize);
-}
-
-void ASTContextStateRecovery::commit() {}
-} // end namespace clang
\ No newline at end of file
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index ab8e418e8d731..01d3295d1ac30 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -20,14 +20,12 @@ if (EMSCRIPTEN AND "lld" IN_LIST LLVM_ENABLE_PROJECTS)
 endif()
 
 add_clang_library(clangInterpreter
-  ASTContextStateStash.cpp
   DeviceOffload.cpp
   CodeCompletion.cpp
   IncrementalAction.cpp
   IncrementalExecutor.cpp
   OrcIncrementalExecutor.cpp
   IncrementalParser.cpp
-  SemaStateStash.cpp
   Interpreter.cpp
   InterpreterValuePrinter.cpp
   InterpreterUtils.cpp
diff --git a/clang/lib/Interpreter/IncrementalAction.h b/clang/lib/Interpreter/IncrementalAction.h
index c3e4f108ea365..2893ff7b5baa6 100644
--- a/clang/lib/Interpreter/IncrementalAction.h
+++ b/clang/lib/Interpreter/IncrementalAction.h
@@ -36,7 +36,6 @@ class IncrementalAction : public WrapperFrontendAction {
   bool IsTerminating = false;
   Interpreter &Interp;
   [[maybe_unused]] CompilerInstance &CI;
-  // llvm::LLVMContext &LLVMCtx;
   std::unique_ptr<ASTConsumer> Consumer;
 
   /// When CodeGen is created the first llvm::Module gets cached in many places
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index ed8919d8af59e..fac67bf00ed14 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -93,7 +93,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
   DiagnosticsEngine &Diags = S.getDiagnostics();
   if (Diags.hasErrorOccurred()) {
     CleanUpPTU(C.getTranslationUnitDecl());
-    // Consumer->HandleTranslationUnit(C);
+
     Diags.Reset(/*soft=*/true);
     Diags.getClient()->clear();
     return llvm::make_error<llvm::StringError>("Parsing failed.",
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index bf9d20653727a..c03881b0ed336 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -568,37 +568,29 @@ size_t Interpreter::getEffectivePTUSize() const {
 
 llvm::Expected<PartialTranslationUnit &>
 Interpreter::Parse(llvm::StringRef Code) {
-  class PTUSlabRollback {
+  class PTURollbackGuard {
   public:
-    explicit PTUSlabRollback(Sema &S)
-        : Ctx(S.getASTContext()), ASTCtxState(Ctx), SemaState(S),
+    explicit PTURollbackGuard(Sema &S)
+        : Ctx(S.getASTContext()),
           CheckPoint(Ctx.getAllocator().checkPoint()) {
-            SemaState.stash(SemaCheckPoint);
-            ASTCtxState.stash(CtxCheckPoint);
           }
 
-    ~PTUSlabRollback() {
+    ~PTURollbackGuard() {
       if (!Committed) {
-        SemaState.restore(SemaCheckPoint, CheckPoint);
-        ASTCtxState.restore(CtxCheckPoint, CheckPoint);
-        Ctx.getAllocator().restoreToCheckPoint(CheckPoint);
+        // TODO add memory restore support.
+        // Ctx.getAllocator().restoreToCheckPoint(CheckPoint);
       }
     }
 
     void commit(PartialTranslationUnit &PTU) {
-      ASTCtxState.commit();
       PTU.SlabCheckPoint = CheckPoint;
       Committed = true;
     }
 
   private:
     ASTContext &Ctx;
-    ASTContextStateRecovery ASTCtxState;
-    SemaStateRecovery SemaState;
     llvm::SlabCheckPoint CheckPoint;
     bool Committed = false;
-    StashCheckPoint CtxCheckPoint;
-    SemaStashCheckPoint SemaCheckPoint;
   };
 
   // If we have a device parser, parse it first. The generated code will be
@@ -627,10 +619,8 @@ Interpreter::Parse(llvm::StringRef Code) {
   // PTUSlabRollback Rollback(CI->getSema());
 
   llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
-  if (!TuOrErr) {
-    // Act->GenModule();
+  if (!TuOrErr)
     return TuOrErr.takeError();
-  }
 
   PartialTranslationUnit &LastPTU = IncrParser->RegisterPTU(*TuOrErr);
 
diff --git a/clang/lib/Interpreter/SemaStateStash.cpp b/clang/lib/Interpreter/SemaStateStash.cpp
deleted file mode 100644
index 1c24b634b9443..0000000000000
--- a/clang/lib/Interpreter/SemaStateStash.cpp
+++ /dev/null
@@ -1,918 +0,0 @@
-//===--- SemaStateRecovery.cpp - Sema persistent state stash/restore
-//----------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Interpreter/ErrorRecovery.h"
-
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/DeclTemplate.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/ExprCXX.h"
-#include "clang/AST/Type.h"
-#include "clang/Basic/IdentifierTable.h"
-#include "clang/Sema/Sema.h"
-#include "clang/Sema/SemaInternal.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include "llvm/ADT/SmallSet.h"
-#include "llvm/ADT/SmallVector.h"
-
-namespace clang {
-
-template <typename EntryType, typename PredT>
-static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
-  SmallVector<EntryType *, 16> ToRemove;
-  for (auto &N : FS)
-    if (Pred(N))
-      ToRemove.push_back(&N);
-  for (auto *N : ToRemove)
-    FS.RemoveNode(N);
-}
-
-// template <typename EntryType, typename PredT>
-// static void eraseContextualFoldingSetIf(
-//     llvm::ContextualFoldingSet<EntryType, const ASTContext &> &FS,
-//     PredT &&Pred) {
-//   SmallVector<EntryType *, 16> ToRemove;
-//   for (auto &N : FS)
-//     if (Pred(N))
-//       ToRemove.push_back(&N);
-//   for (auto *N : ToRemove)
-//     FS.RemoveNode(N);
-// }
-
-/// Erase from DenseMap based on predicate
-template <typename KeyT, typename ValueT, typename PredT>
-static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
-  SmallVector<KeyT, 16> ToRemove;
-
-  for (auto &KV : Map)
-    if (Pred(KV))
-      ToRemove.push_back(KV.getFirst());
-
-  for (auto &Key : ToRemove)
-    Map.erase(Key);
-}
-
-// template <typename ValueT, typename PredT>
-// static void eraseDenseSetIf(llvm::DenseSet<ValueT> &Set, PredT &&Pred) {
-//   SmallVector<ValueT, 16> ToRemove;
-//   for (const auto &Val : Set)
-//     if (Pred(Val))
-//       ToRemove.push_back(Val);
-//   for (const auto &Val : ToRemove)
-//     Set.erase(Val);
-// }
-
-template <typename T, unsigned SmallSize, typename PredT>
-static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, SmallSize> &Set,
-                               PredT &&Pred) {
-  SmallVector<T, 8> ToRemove;
-  for (T Val : Set)
-    if (Pred(Val))
-      ToRemove.push_back(Val);
-  for (T Val : ToRemove)
-    Set.erase(Val);
-}
-
-// template <typename T, unsigned N, typename PredT>
-// static void eraseSmallSetVectorIf(llvm::SmallSetVector<T, N> &SV,
-//                                   PredT &&Pred) {
-//   SmallVector<T, 8> ToRemove;
-//   for (const T &Val : SV)
-//     if (Pred(Val))
-//       ToRemove.push_back(Val);
-//   for (const T &Val : ToRemove)
-//     SV.remove(Val);
-// }
-
-// template <typename KeyT, typename ValueT, typename PredT>
-// static void eraseMapVectorIf(llvm::MapVector<KeyT, ValueT> &MV, PredT &&Pred)
-// {
-//   SmallVector<KeyT, 16> ToRemove;
-//   for (auto &KV : MV)
-//     if (Pred(KV))
-//       ToRemove.push_back(KV.first);
-//   for (const auto &Key : ToRemove)
-//     MV.erase(Key);
-// }
-
-// template <typename T, typename PredT>
-// static void eraseVectorIf(SmallVectorImpl<T> &Vec, PredT &&Pred) {
-//   llvm::erase_if(Vec, std::forward<PredT>(Pred));
-// }
-
-//===----------------------------------------------------------------------===//
-// Pragma / value snapshot (uses Sema friend access for nested types)
-//===----------------------------------------------------------------------===//
-
-// struct SemaStateRecovery::PragmaSnapshot {
-//   Sema::PragmaClangSection PragmaClangBSSSection;
-//   Sema::PragmaClangSection PragmaClangDataSection;
-//   Sema::PragmaClangSection PragmaClangRodataSection;
-//   Sema::PragmaClangSection PragmaClangRelroSection;
-//   Sema::PragmaClangSection PragmaClangTextSection;
-
-//   Sema::PragmaStack<MSVtorDispMode> VtorDispStack;
-//   Sema::PragmaStack<Sema::AlignPackInfo> AlignPackStack;
-//   Sema::PragmaStack<StringLiteral *> DataSegStack;
-//   Sema::PragmaStack<StringLiteral *> BSSSegStack;
-//   Sema::PragmaStack<StringLiteral *> ConstSegStack;
-//   Sema::PragmaStack<StringLiteral *> CodeSegStack;
-//   Sema::PragmaStack<bool> StrictGuardStackCheckStack;
-//   Sema::PragmaStack<FPOptionsOverride> FpPragmaStack;
-
-//   StringLiteral *CurInitSeg = nullptr;
-//   SourceLocation CurInitSegLoc;
-//   bool MSPragmaOptimizeIsOn = true;
-//   SourceLocation OptimizeOffPragmaLocation;
-
-//   FileNullabilityMap NullabilityMap;
-// };
-
-void SemaStateRecovery::stash(SemaStashCheckPoint &CP) {
-  CP.SemaBumpSlabCP = S.BumpAlloc.checkPoint();
-  // CP.CachedFunctionScopeSize = S.CachedFunctionScope.size();
-  CP.FunctionScopesSize = S.FunctionScopes.size();
-
-  // CP.Ident_superSize = S.Ident_super.size();
-
-  /// --- Pragma ---
-  // CP.PragmaClangBSSSectionSize = S.PragmaClangBSSSection.size();
-  // CP.PragmaClangDataSectionSize = S.PragmaClangDataSection.size();
-  // CP.PragmaClangRodataSectionSize = S.PragmaClangRodataSection.size();
-  // CP.PragmaClangRelroSectionSize = S.PragmaClangRelroSection.size();
-  // CP.PragmaClangTextSectionSize = S.PragmaClangTextSection.size();
-  // CP.VtorDispStackSize = S.VtorDispStack.size();
-  // CP.AlignPackStackSize = S.AlignPackStack.size();
-  // CP.AlignPackIncludeStackSize = S.AlignPackIncludeStack.size();
-  // CP.DataSegStackSize = S.DataSegStack.size();
-  // CP.BSSSegStackSize = S.BSSSegStack.size();
-  // CP.ConstSegStackSize = S.ConstSegStack.size();
-  // CP.CodeSegStackSize = S.CodeSegStack.size();
-  // CP.StrictGuardStackCheckStackSize = S.StrictGuardStackCheckStack.size();
-  // CP.FpPragmaStackSize = S.FpPragmaStack.size();
-
-  // CP.FunctionToSectionMapSize = S.FunctionToSectionMap.size();
-  // CP.PragmaAttributeStackSize = S.PragmaAttributeStack.size();
-  // CP.MSFunctionNoBuiltinsSize = S.MSFunctionNoBuiltins.size();
-  // CP.PendingExportedNamesSize = S.PendingExportedNames.size();
-  CP.TypoCorrectedFunctionDefinitionsSize =
-      S.TypoCorrectedFunctionDefinitions.size();
-  CP.FlagBitsCacheSize = S.FlagBitsCache.size();
-  CP.AssignEnumCacheSize = S.AssignEnumCache.size();
-  CP.WeakUndeclaredIdentifiersSize = S.WeakUndeclaredIdentifiers.size();
-  CP.ExtnameUndeclaredIdentifiersSize = S.ExtnameUndeclaredIdentifiers.size();
-  CP.UnusedLocalTypedefNameCandidatesSize =
-      S.UnusedLocalTypedefNameCandidates.size();
-  CP.UnusedFileScopedDeclsSize = S.UnusedFileScopedDecls.end();
-  CP.TentativeDefinitionsSize = S.TentativeDefinitions.end();
-  CP.ExternalDeclarationsSize = S.ExternalDeclarations.size();
-  CP.ParsingInitForAutoVarsSize = S.ParsingInitForAutoVars.size();
-  CP.DeclsToCheckForDeferredDiagsSize = S.DeclsToCheckForDeferredDiags.size();
-  CP.ShadowingDeclsSize = S.ShadowingDecls.size();
-  CP.WeakTopLevelDeclSize = S.WeakTopLevelDecl.size();
-  CP.ExtVectorDeclsSize = S.ExtVectorDecls.end();
-  CP.VTableUsesSize = S.VTableUses.size();
-  CP.VTablesUsedSize = S.VTablesUsed.size();
-  CP.DelayedDllExportClassesSize = S.DelayedDllExportClasses.size();
-  CP.DelayedDllExportMemberFunctionsSize =
-      S.DelayedDllExportMemberFunctions.size();
-  CP.InventedParameterInfosSize = S.InventedParameterInfos.size();
-  // CP.FieldCollectorSize = S.FieldCollector.size();
-  CP.UnusedPrivateFieldsSize = S.UnusedPrivateFields.size();
-  if (S.PureVirtualClassDiagSet)
-    CP.PureVirtualClassDiagSetSize = S.PureVirtualClassDiagSet->size();
-  CP.DelegatingCtorDeclsSize = S.DelegatingCtorDecls.end();
-  // CP.StdNamespaceSize = S.StdNamespace.size();
-  CP.UnparsedDefaultArgLocsSize = S.UnparsedDefaultArgLocs.size();
-  CP.UndefinedButUsedSize = S.UndefinedButUsed.size();
-  CP.SpecialMembersBeingDeclaredSize = S.SpecialMembersBeingDeclared.size();
-  CP.DelayedOverridingExceptionSpecChecksSize =
-      S.DelayedOverridingExceptionSpecChecks.size();
-  CP.DelayedEquivalentExceptionSpecChecksSize =
-      S.DelayedEquivalentExceptionSpecChecks.size();
-  CP.MaybeODRUseExprsSize = S.MaybeODRUseExprs.size();
-  CP.RefsMinusAssignmentsSize = S.RefsMinusAssignments.size();
-  CP.ExprCleanupObjectsSize = S.ExprCleanupObjects.size();
-  CP.ExprEvalContextsSize = S.ExprEvalContexts.size();
-  CP.FailedImmediateInvocationsSize = S.FailedImmediateInvocations.size();
-  // CP.ImplicitlyRetainedSelfLocsSize = S.ImplicitlyRetainedSelfLocs.size();
-  CP.DeleteExprsSize = S.DeleteExprs.size();
-  CP.CurrentParameterCopyTypesSize = S.CurrentParameterCopyTypes.size();
-  CP.AggregateDeductionCandidatesSize = S.AggregateDeductionCandidates.size();
-  CP.TypoCorrectionFailuresSize = S.TypoCorrectionFailures.size();
-  CP.SpecialMemberCacheSize = S.SpecialMemberCache.size();
-  CP.ModuleScopesSize = S.ModuleScopes.size();
-  CP.DeferredExportedNamespacesSize = S.DeferredExportedNamespaces.size();
-  CP.PendingInlineFuncDeclsSize = S.PendingInlineFuncDecls.size();
-  CP.CurrentSEHFinallySize = S.CurrentSEHFinally.size();
-  CP.CurrentDeferSize = S.CurrentDefer.size();
-  CP.LateParsedTemplateMapSize = S.LateParsedTemplateMap.size();
-  CP.SuppressedDiagnosticsSize = S.SuppressedDiagnostics.size();
-  // CP.CurrentInstantiationScopeSize = S.CurrentInstantiationScope.size();
-  CP.UnparsedDefaultArgInstantiationsSize =
-      S.UnparsedDefaultArgInstantiations.size();
-  CP.CodeSynthesisContextsSize = S.CodeSynthesisContexts.size();
-  CP.InstantiatingSpecializationsSize = S.InstantiatingSpecializations.size();
-  CP.InstantiatedNonDependentTypesSize = S.InstantiatedNonDependentTypes.size();
-  CP.CodeSynthesisContextLookupModulesSize =
-      S.CodeSynthesisContextLookupModules.size();
-  CP.LookupModulesCacheSize = S.LookupModulesCache.size();
-  CP.VisibleNamespaceCacheSize = S.VisibleNamespaceCache.size();
-  // CP.TemplateInstCallbacksSize = S.TemplateInstCallbacks.size();
-  CP.PendingInstantiationsSize = S.PendingInstantiations.size();
-  CP.LateParsedInstantiationsSize = S.LateParsedInstantiations.size();
-  CP.SavedVTableUsesSize = S.SavedVTableUses.size();
-  CP.SavedPendingInstantiationsSize = S.SavedPendingInstantiations.size();
-  CP.PendingLocalImplicitInstantiationsSize =
-      S.PendingLocalImplicitInstantiations.size();
-  CP.UnsubstitutedConstraintSatisfactionCacheSize =
-      S.UnsubstitutedConstraintSatisfactionCache.size();
-  if (S.CurrentCachedTemplateArgs)
-    CP.CurrentCachedTemplateArgsSize = S.CurrentCachedTemplateArgs->size();
-  CP.SubsumptionCacheSize = S.SubsumptionCache.size();
-  CP.NormalizationCacheSize = S.NormalizationCache.size();
-  CP.SatisfactionCacheSize = S.SatisfactionCache.size();
-  CP.SatisfactionStackSize = S.SatisfactionStack.size();
-  // CP.NullabilityMapSize = S.NullabilityMap.size();
-  CP.DeclsWithEffectsToVerifySize = S.DeclsWithEffectsToVerify.size();
-  // CP.AllEffectsToVerifySize = S.AllEffectsToVerify.size();
-}
-
-void SemaStateRecovery::restore(SemaStashCheckPoint &CP,
-                                llvm::SlabCheckPoint SlabCP) {
-
-  ASTContext &Ctx = S.getASTContext();
-  if (CP.FunctionScopesSize != S.FunctionScopes.size()) {
-    llvm::dbgs() << "CP.FunctionScopesSize != S.FunctionScopes.size()\n";
-    S.FunctionScopes.resize(CP.FunctionScopesSize);
-    assert(CP.FunctionScopesSize == S.FunctionScopes.size());
-  }
-
-  if (CP.TypoCorrectedFunctionDefinitionsSize !=
-      S.TypoCorrectedFunctionDefinitions.size()) {
-    llvm::dbgs() << "CP.TypoCorrectedFunctionDefinitionsSize != "
-                    "S.TypoCorrectedFunctionDefinitions.size()\n";
-    // llvm::SmallPtrSet<const NamedDecl *, 4> TypoCorrectedFunctionDefinitions;
-    assert(CP.TypoCorrectedFunctionDefinitionsSize ==
-           S.TypoCorrectedFunctionDefinitions.size());
-  }
-
-  if (CP.FlagBitsCacheSize != S.FlagBitsCache.size()) {
-    llvm::dbgs() << "CP.FlagBitsCacheSize != S.FlagBitsCache.size()\n";
-    // mutable llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache;
-    assert(CP.FlagBitsCacheSize == S.FlagBitsCache.size());
-  }
-
-  if (CP.AssignEnumCacheSize != S.AssignEnumCache.size()) {
-    llvm::dbgs() << "CP.AssignEnumCacheSize != S.AssignEnumCache.size()\n";
-    //   llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt>>
-    //   AssignEnumCache;
-    assert(CP.AssignEnumCacheSize == S.AssignEnumCache.size());
-  }
-
-  if (CP.WeakUndeclaredIdentifiersSize != S.WeakUndeclaredIdentifiers.size()) {
-    llvm::dbgs() << "CP.WeakUndeclaredIdentifiersSize != "
-                    "S.WeakUndeclaredIdentifiers.size()\n";
-    //  llvm::MapVector<
-    //   IdentifierInfo *,
-    //   llvm::SetVector<
-    //       WeakInfo, llvm::SmallVector<WeakInfo, 1u>,
-    //       llvm::SmallDenseSet<WeakInfo, 2u,
-    //       WeakInfo::DenseMapInfoByAliasOnly>>>
-    //   WeakUndeclaredIdentifiers;
-    assert(CP.WeakUndeclaredIdentifiersSize ==
-           S.WeakUndeclaredIdentifiers.size());
-  }
-
-  if (CP.ExtnameUndeclaredIdentifiersSize !=
-      S.ExtnameUndeclaredIdentifiers.size()) {
-    llvm::dbgs() << "CP.ExtnameUndeclaredIdentifiersSize != "
-                    "S.ExtnameUndeclaredIdentifiers.size()\n";
-    //   llvm::DenseMap<IdentifierInfo *, AsmLabelAttr *>
-    //   ExtnameUndeclaredIdentifiers;
-    assert(CP.ExtnameUndeclaredIdentifiersSize ==
-           S.ExtnameUndeclaredIdentifiers.size());
-  }
-
-  if (CP.UnusedLocalTypedefNameCandidatesSize !=
-      S.UnusedLocalTypedefNameCandidates.size()) {
-    llvm::dbgs() << "CP.UnusedLocalTypedefNameCandidatesSize != "
-                    "S.UnusedLocalTypedefNameCandidates.size()\n";
-    //   llvm::SmallSetVector<const TypedefNameDecl *, 4>
-    //   UnusedLocalTypedefNameCandidates;
-    assert(CP.UnusedLocalTypedefNameCandidatesSize ==
-           S.UnusedLocalTypedefNameCandidates.size());
-  }
-
-  if (CP.UnusedFileScopedDeclsSize != S.UnusedFileScopedDecls.end()) {
-    llvm::dbgs()
-        << "CP.UnusedFileScopedDeclsSize != S.UnusedFileScopedDecls.end()\n";
-    //  typedef LazyVector<const DeclaratorDecl *, ExternalSemaSource,
-    //  &ExternalSemaSource::ReadUnusedFileScopedDecls, 2, 2>
-    //  UnusedFileScopedDeclsType;
-    assert(CP.UnusedFileScopedDeclsSize == S.UnusedFileScopedDecls.end());
-  }
-
-  if (CP.TentativeDefinitionsSize != S.TentativeDefinitions.end()) {
-    llvm::dbgs()
-        << "CP.TentativeDefinitionsSize != S.TentativeDefinitions.size()\n";
-    //    typedef LazyVector<VarDecl *, ExternalSemaSource,
-    //                  &ExternalSemaSource::ReadTentativeDefinitions, 2, 2>
-    //   TentativeDefinitionsType;
-    assert(CP.TentativeDefinitionsSize == S.TentativeDefinitions.end());
-  }
-
-  if (CP.ExternalDeclarationsSize != S.ExternalDeclarations.size()) {
-    llvm::dbgs()
-        << "CP.ExternalDeclarationsSize != S.ExternalDeclarations.size()\n";
-    //    SmallVector<DeclaratorDecl *, 4> ExternalDeclarations;
-    assert(CP.ExternalDeclarationsSize == S.ExternalDeclarations.size());
-  }
-
-  if (CP.ParsingInitForAutoVarsSize != S.ParsingInitForAutoVars.size()) {
-    llvm::dbgs()
-        << "CP.ParsingInitForAutoVarsSize != S.ParsingInitForAutoVars.size()\n";
-    //   llvm::SmallPtrSet<const Decl *, 4> ParsingInitForAutoVars;
-    assert(CP.ParsingInitForAutoVarsSize == S.ParsingInitForAutoVars.size());
-  }
-
-  if (CP.DeclsToCheckForDeferredDiagsSize !=
-      S.DeclsToCheckForDeferredDiags.size()) {
-    llvm::dbgs() << "CP.DeclsToCheckForDeferredDiagsSize != "
-                    "S.DeclsToCheckForDeferredDiags.size()\n";
-    //   llvm::SmallSetVector<Decl *, 4> DeclsToCheckForDeferredDiags;
-    assert(CP.DeclsToCheckForDeferredDiagsSize ==
-           S.DeclsToCheckForDeferredDiags.size());
-  }
-
-  if (CP.ShadowingDeclsSize != S.ShadowingDecls.size()) {
-    llvm::dbgs() << "CP.ShadowingDeclsSize != S.ShadowingDecls.size()\n";
-    //   llvm::DenseMap<const NamedDecl *, const NamedDecl *> ShadowingDecls;
-    assert(CP.ShadowingDeclsSize == S.ShadowingDecls.size());
-  }
-
-  if (CP.WeakTopLevelDeclSize != S.WeakTopLevelDecl.size()) {
-    llvm::dbgs() << "CP.WeakTopLevelDeclSize != S.WeakTopLevelDecl.size()\n";
-    //    SmallVector<Decl *, 2> WeakTopLevelDecl;
-    assert(CP.WeakTopLevelDeclSize == S.WeakTopLevelDecl.size());
-  }
-
-  if (CP.ExtVectorDeclsSize != S.ExtVectorDecls.end()) {
-    llvm::dbgs() << "CP.ExtVectorDeclsSize != S.ExtVectorDecls.size()\n";
-    //   typedef LazyVector<TypedefNameDecl *, ExternalSemaSource,
-    //  &ExternalSemaSource::ReadExtVectorDecls, 2, 2> ExtVectorDecls;
-    assert(CP.ExtVectorDeclsSize == S.ExtVectorDecls.end());
-  }
-
-  if (CP.VTableUsesSize != S.VTableUses.size()) {
-    llvm::dbgs() << "CP.VTableUsesSize != S.VTableUses.size()\n";
-    //   SmallVector<VTableUse, 16> VTableUses;
-    assert(CP.VTableUsesSize == S.VTableUses.size());
-  }
-
-  if (CP.VTablesUsedSize != S.VTablesUsed.size()) {
-    llvm::dbgs() << "CP.VTablesUsedSize != S.VTablesUsed.size()\n";
-    //   llvm::DenseMap<CXXRecordDecl *, bool> VTablesUsed;
-    eraseDenseMapIf(
-        S.VTablesUsed,
-        [&](llvm::detail::DenseMapPair<CXXRecordDecl *, bool> &KV) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-              static_cast<void *>(KV.getFirst()), SlabCP);
-        });
-    // assert(CP.VTablesUsedSize == S.VTablesUsed.size());
-  }
-
-  if (CP.DelayedDllExportClassesSize != S.DelayedDllExportClasses.size()) {
-    llvm::dbgs() << "CP.DelayedDllExportClassesSize != "
-                    "S.DelayedDllExportClasses.size()\n";
-    //   SmallVector<CXXRecordDecl *, 4> DelayedDllExportClasses;
-    assert(CP.DelayedDllExportClassesSize == S.DelayedDllExportClasses.size());
-  }
-
-  if (CP.DelayedDllExportMemberFunctionsSize !=
-      S.DelayedDllExportMemberFunctions.size()) {
-    llvm::dbgs() << "CP.DelayedDllExportMemberFunctionsSize != "
-                    "S.DelayedDllExportMemberFunctions.size()\n";
-    //   SmallVector<CXXMethodDecl *, 4> DelayedDllExportMemberFunctions;
-    assert(CP.DelayedDllExportMemberFunctionsSize ==
-           S.DelayedDllExportMemberFunctions.size());
-  }
-
-  if (CP.InventedParameterInfosSize != S.InventedParameterInfos.size()) {
-    llvm::dbgs()
-        << "CP.InventedParameterInfosSize != S.InventedParameterInfos.size()\n";
-    //   SmallVector<InventedTemplateParameterInfo, 4> InventedParameterInfos;
-    assert(CP.InventedParameterInfosSize == S.InventedParameterInfos.size());
-  }
-
-  if (CP.UnusedPrivateFieldsSize != S.UnusedPrivateFields.size()) {
-    llvm::dbgs()
-        << "CP.UnusedPrivateFieldsSize != S.UnusedPrivateFields.size()\n";
-    //   typedef llvm::SmallSetVector<const NamedDecl *, 16> NamedDeclSetType;
-    /// Set containing all declared private fields that are not used.
-    //   NamedDeclSetType UnusedPrivateFields;
-    assert(CP.UnusedPrivateFieldsSize == S.UnusedPrivateFields.size());
-  }
-
-  if (S.PureVirtualClassDiagSet &&
-      (CP.PureVirtualClassDiagSetSize != S.PureVirtualClassDiagSet->size())) {
-    llvm::dbgs() << "CP.PureVirtualClassDiagSetSize != "
-                    "S.PureVirtualClassDiagSet.size()\n";
-    //   typedef llvm::SmallPtrSet<const CXXRecordDecl *, 8> RecordDeclSetTy;
-
-    /// PureVirtualClassDiagSet - a set of class declarations which we have
-    /// emitted a list of pure virtual functions. Used to prevent emitting the
-    /// same list more than once.
-    //   std::unique_ptr<RecordDeclSetTy> PureVirtualClassDiagSet;
-    eraseSmallPtrSetIf(
-        *S.PureVirtualClassDiagSet.get(), [&](const CXXRecordDecl *RD) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(RD, SlabCP);
-        });
-    // assert(CP.PureVirtualClassDiagSetSize ==
-    // S.PureVirtualClassDiagSet->size());
-  }
-
-  if (CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.end()) {
-    llvm::dbgs()
-        << "CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.size()\n";
-    //   typedef LazyVector<CXXConstructorDecl *, ExternalSemaSource,
-    //  &ExternalSemaSource::ReadDelegatingConstructors, 2, 2>
-    //   DelegatingCtorDeclsType;
-
-    /// All the delegating constructors seen so far in the file, used for
-    /// cycle detection at the end of the TU.
-    //   DelegatingCtorDeclsType DelegatingCtorDecls;
-
-    assert(CP.DelegatingCtorDeclsSize == S.DelegatingCtorDecls.end());
-  }
-
-  if (CP.UnparsedDefaultArgLocsSize != S.UnparsedDefaultArgLocs.size()) {
-    llvm::dbgs()
-        << "CP.UnparsedDefaultArgLocsSize != S.UnparsedDefaultArgLocs.size()\n";
-    //   llvm::DenseMap<ParmVarDecl *, SourceLocation> UnparsedDefaultArgLocs;
-    assert(CP.UnparsedDefaultArgLocsSize == S.UnparsedDefaultArgLocs.size());
-  }
-
-  if (CP.UndefinedButUsedSize != S.UndefinedButUsed.size()) {
-    llvm::dbgs() << "CP.UndefinedButUsedSize != S.UndefinedButUsed.size()\n";
-    // llvm::MapVector<NamedDecl *, SourceLocation> UndefinedButUsed;
-    while (CP.UndefinedButUsedSize != S.UndefinedButUsed.size())
-      S.UndefinedButUsed.pop_back();
-    assert(CP.UndefinedButUsedSize == S.UndefinedButUsed.size());
-  }
-
-  if (CP.SpecialMembersBeingDeclaredSize !=
-      S.SpecialMembersBeingDeclared.size()) {
-    llvm::dbgs() << "CP.SpecialMembersBeingDeclaredSize != "
-                    "S.SpecialMembersBeingDeclared.size()\n";
-    //   llvm::SmallPtrSet<SpecialMemberDecl, 4> SpecialMembersBeingDeclared;
-    assert(CP.SpecialMembersBeingDeclaredSize ==
-           S.SpecialMembersBeingDeclared.size());
-  }
-
-  if (CP.SpecialMembersBeingDeclaredSize !=
-      S.SpecialMembersBeingDeclared.size()) {
-    llvm::dbgs() << "CP.SpecialMembersBeingDeclaredSize != "
-                    "S.SpecialMembersBeingDeclared.size()\n";
-    //   llvm::SmallPtrSet<SpecialMemberDecl, 4> SpecialMembersBeingDeclared;
-    assert(CP.SpecialMembersBeingDeclaredSize ==
-           S.SpecialMembersBeingDeclared.size());
-  }
-
-  if (CP.DelayedOverridingExceptionSpecChecksSize !=
-      S.DelayedOverridingExceptionSpecChecks.size()) {
-    llvm::dbgs() << "CP.DelayedOverridingExceptionSpecChecksSize != "
-                    "S.DelayedOverridingExceptionSpecChecks.size()\n";
-    //     SmallVector<std::pair<const CXXMethodDecl *, const CXXMethodDecl *>,
-    //     2>
-    //   DelayedOverridingExceptionSpecChecks;
-    assert(CP.DelayedOverridingExceptionSpecChecksSize ==
-           S.DelayedOverridingExceptionSpecChecks.size());
-  }
-
-  if (CP.DelayedEquivalentExceptionSpecChecksSize !=
-      S.DelayedEquivalentExceptionSpecChecks.size()) {
-    llvm::dbgs() << "CP.DelayedEquivalentExceptionSpecChecksSize != "
-                    "S.DelayedEquivalentExceptionSpecChecks.size()\n";
-    //   SmallVector<std::pair<FunctionDecl *, FunctionDecl *>, 2>
-    //   DelayedEquivalentExceptionSpecChecks;
-    assert(CP.DelayedEquivalentExceptionSpecChecksSize ==
-           S.DelayedEquivalentExceptionSpecChecks.size());
-  }
-
-  if (CP.MaybeODRUseExprsSize != S.MaybeODRUseExprs.size()) {
-    llvm::dbgs() << "CP.MaybeODRUseExprsSize != S.MaybeODRUseExprs.size()\n";
-    // S.MaybeODRUseExprs.resize(CP.MaybeODRUseExprsSize);
-    assert(CP.MaybeODRUseExprsSize == S.MaybeODRUseExprs.size());
-  }
-
-  if (CP.RefsMinusAssignmentsSize != S.RefsMinusAssignments.size()) {
-    llvm::dbgs()
-        << "CP.RefsMinusAssignmentsSize != S.RefsMinusAssignments.size()\n";
-    //     llvm::DenseMap<const VarDecl *, int> RefsMinusAssignments;
-    eraseDenseMapIf(
-        S.RefsMinusAssignments,
-        [&](llvm::detail::DenseMapPair<const VarDecl *, int> &KV) -> bool {
-          return Ctx.getAllocator().isAfterCheckpoint(
-              static_cast<void *>(const_cast<VarDecl *>(KV.getFirst())),
-              SlabCP);
-        });
-    assert(CP.RefsMinusAssignmentsSize == S.RefsMinusAssignments.size());
-  }
-
-  if (CP.ExprCleanupObjectsSize != S.ExprCleanupObjects.size()) {
-    llvm::dbgs()
-        << "CP.ExprCleanupObjectsSize != S.ExprCleanupObjects.size()\n";
-    //   SmallVector<ExprWithCleanups::CleanupObject, 8> ExprCleanupObjects;
-    assert(CP.ExprCleanupObjectsSize == S.ExprCleanupObjects.size());
-  }
-
-  if (CP.ExprEvalContextsSize != S.ExprEvalContexts.size()) {
-    llvm::dbgs() << "CP.ExprEvalContextsSize != S.ExprEvalContexts.size()\n";
-    //   SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts;
-    assert(CP.ExprEvalContextsSize == S.ExprEvalContexts.size());
-  }
-
-  if (CP.FailedImmediateInvocationsSize !=
-      S.FailedImmediateInvocations.size()) {
-    llvm::dbgs() << "CP.FailedImmediateInvocationsSize != "
-                    "S.FailedImmediateInvocations.size()\n";
-    //    llvm::SmallPtrSet<ConstantExpr *, 4> FailedImmediateInvocations;
-    assert(CP.FailedImmediateInvocationsSize ==
-           S.FailedImmediateInvocations.size());
-  }
-
-  if (CP.DeleteExprsSize != S.DeleteExprs.size()) {
-    llvm::dbgs() << "CP.DeleteExprsSize != S.DeleteExprs.size()\n";
-    //   llvm::MapVector<FieldDecl *, DeleteLocs> DeleteExprs;
-    assert(CP.DeleteExprsSize == S.DeleteExprs.size());
-  }
-
-  if (CP.CurrentParameterCopyTypesSize != S.CurrentParameterCopyTypes.size()) {
-    llvm::dbgs() << "CP.CurrentParameterCopyTypesSize != "
-                    "S.CurrentParameterCopyTypes.size()\n";
-    //    llvm::SmallVector<QualType, 4> CurrentParameterCopyTypes;
-    assert(CP.CurrentParameterCopyTypesSize ==
-           S.CurrentParameterCopyTypes.size());
-  }
-
-  if (CP.AggregateDeductionCandidatesSize !=
-      S.AggregateDeductionCandidates.size()) {
-    llvm::dbgs() << "CP.AggregateDeductionCandidatesSize != "
-                    "S.AggregateDeductionCandidates.size()\n";
-    //   llvm::DenseMap<unsigned, CXXDeductionGuideDecl *>
-    //   AggregateDeductionCandidates;
-    assert(CP.AggregateDeductionCandidatesSize ==
-           S.AggregateDeductionCandidates.size());
-  }
-
-  if (CP.TypoCorrectionFailuresSize != S.TypoCorrectionFailures.size()) {
-    llvm::dbgs() << "CP.TypoCorrectionFailuresSize != "
-                    "S.TypoCorrectionFailures.size()\n";
-    // eraseDenseMapIf(
-    //     S.TypoCorrectionFailures,
-    //     [&](llvm::detail::DenseMapPair<IdentifierInfo *, Sema::SrcLocSet>
-    //     &KV)
-    //         -> bool {
-    //           llvm::outs() << "SlabCheckPoint Cur : " << (void
-    //           *)SlabCP.CurPtr << "\n"; llvm::outs() << "SlabCheckPoint END :
-    //           " << (void *)SlabCP.End << "\n"; llvm::outs() << "Addr: " <<
-    //           static_cast<void *>(KV.getFirst()) << "\n";
-    //       return Ctx.getAllocator().isAfterCheckpoint(
-    //           static_cast<void *>(KV.getFirst()), SlabCP);
-    //     });
-    // assert(CP.TypoCorrectionFailuresSize == S.TypoCorrectionFailures.size());
-  }
-
-  if (CP.ModuleScopesSize != S.ModuleScopes.size()) {
-    llvm::dbgs() << "CP.ModuleScopesSize != "
-                    "S.ModuleScopes.size()\n";
-    //  llvm::SmallVector<ModuleScope, 16> ModuleScopes;
-    assert(CP.ModuleScopesSize == S.ModuleScopes.size());
-  }
-
-  if (CP.DeferredExportedNamespacesSize !=
-      S.DeferredExportedNamespaces.size()) {
-    llvm::dbgs() << "CP.DeferredExportedNamespacesSize != "
-                    "S.DeferredExportedNamespaces.size()\n";
-    /// Namespace definitions that we will export when they finish.
-    // llvm::SmallPtrSet<const NamespaceDecl *, 8> DeferredExportedNamespaces;
-    assert(CP.DeferredExportedNamespacesSize ==
-           S.DeferredExportedNamespaces.size());
-  }
-
-  if (CP.PendingInlineFuncDeclsSize != S.PendingInlineFuncDecls.size()) {
-    llvm::dbgs() << "CP.PendingInlineFuncDeclsSize != "
-                    "S.PendingInlineFuncDecls.size()\n";
-    ///   /// In a C++ standard module, inline declarations require a definition
-    ///   to be
-    /// present at the end of a definition domain.  This set holds the decls to
-    /// be checked at the end of the TU.
-    //   llvm::SmallPtrSet<const FunctionDecl *, 8> PendingInlineFuncDecls;
-
-    assert(CP.PendingInlineFuncDeclsSize == S.PendingInlineFuncDecls.size());
-  }
-
-  if (CP.CurrentSEHFinallySize != S.CurrentSEHFinally.size()) {
-    llvm::dbgs() << "CP.CurrentSEHFinallySize != "
-                    "S.CurrentSEHFinally.size()\n";
-    //    /// Stack of active SEH __finally scopes.  Can be empty.
-    // SmallVector<Scope *, 2> CurrentSEHFinally;
-
-    assert(CP.CurrentSEHFinallySize == S.CurrentSEHFinally.size());
-  }
-
-  if (CP.CurrentDeferSize != S.CurrentDefer.size()) {
-    llvm::dbgs() << "CP.CurrentDeferSize != "
-                    "S.CurrentDefer.size()\n";
-    //    /// Stack of '_Defer' statements that are currently being parsed, as
-    //    well
-    /// as the locations of their '_Defer' keywords. Can be empty.
-    //   SmallVector<std::pair<Scope *, SourceLocation>, 2> CurrentDefer;
-
-    assert(CP.CurrentDeferSize == S.CurrentDefer.size());
-  }
-
-  if (CP.LateParsedTemplateMapSize != S.LateParsedTemplateMap.size()) {
-    llvm::dbgs() << "CP.LateParsedTemplateMapSize != "
-                    "S.LateParsedTemplateMap.size()\n";
-    //   typedef llvm::MapVector<const FunctionDecl *,
-    //                           std::unique_ptr<LateParsedTemplate>>
-    //       LateParsedTemplateMapT;
-    //   LateParsedTemplateMapT LateParsedTemplateMap;
-    assert(CP.LateParsedTemplateMapSize == S.LateParsedTemplateMap.size());
-  }
-
-  if (CP.SuppressedDiagnosticsSize != S.SuppressedDiagnostics.size()) {
-    llvm::dbgs() << "CP.SuppressedDiagnosticsSize != "
-                    "S.SuppressedDiagnostics.size()\n";
-    //    typedef llvm::DenseMap<Decl *, SmallVector<PartialDiagnosticAt, 1>>
-    //       SuppressedDiagnosticsMap;
-    //   SuppressedDiagnosticsMap SuppressedDiagnostics;
-    assert(CP.SuppressedDiagnosticsSize == S.SuppressedDiagnostics.size());
-  }
-
-  if (CP.UnparsedDefaultArgInstantiationsSize !=
-      S.UnparsedDefaultArgInstantiations.size()) {
-    llvm::dbgs() << "CP.UnparsedDefaultArgInstantiationsSize != "
-                    "S.UnparsedDefaultArgInstantiations.size()\n";
-
-    //   typedef llvm::DenseMap<ParmVarDecl *, llvm::TinyPtrVector<ParmVarDecl
-    //   *>>
-    //       UnparsedDefaultArgInstantiationsMap;
-
-    //   /// A mapping from parameters with unparsed default arguments to the
-    //   /// set of instantiations of each parameter.
-    //   ///
-    //   /// This mapping is a temporary data structure used when parsing
-    //   /// nested class templates or nested classes of class templates,
-    //   /// where we might end up instantiating an inner class before the
-    //   /// default arguments of its methods have been parsed.
-    //   UnparsedDefaultArgInstantiationsMap UnparsedDefaultArgInstantiations;
-
-    assert(CP.UnparsedDefaultArgInstantiationsSize ==
-           S.UnparsedDefaultArgInstantiations.size());
-  }
-
-  if (CP.CodeSynthesisContextsSize != S.CodeSynthesisContexts.size()) {
-    llvm::dbgs() << "CP.CodeSynthesisContextsSize != "
-                    "S.CodeSynthesisContexts.size()\n";
-    //  SmallVector<CodeSynthesisContext, 16> CodeSynthesisContexts;
-
-    assert(CP.CodeSynthesisContextsSize == S.CodeSynthesisContexts.size());
-  }
-
-  if (CP.InstantiatingSpecializationsSize !=
-      S.InstantiatingSpecializations.size()) {
-    llvm::dbgs() << "CP.InstantiatingSpecializationsSize != "
-                    "S.InstantiatingSpecializations.size()\n";
-    //    /// Specializations whose definitions are currently being
-    //    instantiated.
-    //   llvm::DenseSet<InstantiatingSpecializationsKey>
-    //   InstantiatingSpecializations;
-
-    assert(CP.InstantiatingSpecializationsSize ==
-           S.InstantiatingSpecializations.size());
-  }
-
-  if (CP.InstantiatedNonDependentTypesSize !=
-      S.InstantiatedNonDependentTypes.size()) {
-    llvm::dbgs() << "CP.InstantiatedNonDependentTypesSize != "
-                    "S.InstantiatedNonDependentTypes.size()\n";
-    //      llvm::DenseSet<QualType> InstantiatedNonDependentTypes;
-    assert(CP.InstantiatedNonDependentTypesSize ==
-           S.InstantiatedNonDependentTypes.size());
-  }
-
-  if (CP.CodeSynthesisContextLookupModulesSize !=
-      S.CodeSynthesisContextLookupModules.size()) {
-    llvm::dbgs() << "CP.CodeSynthesisContextLookupModulesSize != "
-                    "S.CodeSynthesisContextLookupModules.size()\n";
-    //      SmallVector<Module *, 16> CodeSynthesisContextLookupModules;
-
-    assert(CP.CodeSynthesisContextLookupModulesSize ==
-           S.CodeSynthesisContextLookupModules.size());
-  }
-
-  if (CP.LookupModulesCacheSize != S.LookupModulesCache.size()) {
-    llvm::dbgs() << "CP.LookupModulesCacheSize != "
-                    "S.LookupModulesCache.size()\n";
-    //        llvm::DenseSet<Module *> LookupModulesCache;
-
-    assert(CP.LookupModulesCacheSize == S.LookupModulesCache.size());
-  }
-
-  if (CP.VisibleNamespaceCacheSize != S.VisibleNamespaceCache.size()) {
-    llvm::dbgs() << "CP.VisibleNamespaceCacheSize != "
-                    "S.VisibleNamespaceCache.size()\n";
-    //           llvm::DenseMap<NamedDecl *, NamedDecl *> VisibleNamespaceCache;
-
-    assert(CP.VisibleNamespaceCacheSize == S.VisibleNamespaceCache.size());
-  }
-
-  // if (CP.TemplateInstCallbacksSize != S.TemplateInstCallbacks.size()) {
-  //   llvm::dbgs() << "CP.TemplateInstCallbacksSize != "
-  //                   "S.TemplateInstCallbacks.size()\n";
-  //   //    std::vector<std::unique_ptr<TemplateInstantiationCallback>>
-  //   //   TemplateInstCallbacks;
-  //   assert(CP.TemplateInstCallbacksSize == S.TemplateInstCallbacks.size());
-  // }
-
-  if (CP.PendingInstantiationsSize != S.PendingInstantiations.size()) {
-    llvm::dbgs() << "CP.PendingInstantiationsSize != "
-                    "S.PendingInstantiations.size()\n";
-    //      std::deque<PendingImplicitInstantiation> PendingInstantiations;
-
-    assert(CP.PendingInstantiationsSize == S.PendingInstantiations.size());
-  }
-
-  if (CP.LateParsedInstantiationsSize != S.LateParsedInstantiations.size()) {
-    llvm::dbgs() << "CP.LateParsedInstantiationsSize != "
-                    "S.LateParsedInstantiations.size()\n";
-    S.LateParsedInstantiations.resize(CP.LateParsedInstantiationsSize);
-    assert(CP.LateParsedInstantiationsSize ==
-           S.LateParsedInstantiations.size());
-  }
-
-  if (CP.SavedVTableUsesSize != S.SavedVTableUses.size()) {
-    llvm::dbgs() << "CP.SavedVTableUsesSize != "
-                    "S.SavedVTableUses.size()\n";
-    S.SavedVTableUses.resize(CP.SavedVTableUsesSize);
-    assert(CP.SavedVTableUsesSize == S.SavedVTableUses.size());
-  }
-
-  if (CP.SavedPendingInstantiationsSize !=
-      S.SavedPendingInstantiations.size()) {
-    llvm::dbgs() << "CP.SavedPendingInstantiationsSize != "
-                    "S.SavedPendingInstantiations.size()\n";
-    S.SavedPendingInstantiations.resize(CP.SavedPendingInstantiationsSize);
-    assert(CP.SavedPendingInstantiationsSize ==
-           S.SavedPendingInstantiations.size());
-  }
-
-  if (CP.PendingLocalImplicitInstantiationsSize !=
-      S.PendingLocalImplicitInstantiations.size()) {
-    llvm::dbgs() << "CP.PendingLocalImplicitInstantiationsSize != "
-                    "S.PendingLocalImplicitInstantiations.size()\n";
-    S.PendingLocalImplicitInstantiations.resize(
-        CP.PendingLocalImplicitInstantiationsSize);
-    assert(CP.PendingLocalImplicitInstantiationsSize ==
-           S.PendingLocalImplicitInstantiations.size());
-  }
-
-  if (S.CurrentCachedTemplateArgs && (CP.CurrentCachedTemplateArgsSize !=
-                                      S.CurrentCachedTemplateArgs->size())) {
-    llvm::dbgs() << "CP.CurrentCachedTemplateArgsSize != "
-                    "S.CurrentCachedTemplateArgs->size()\n ";
-    // eraseDenseMapIf(
-    //     S.UnsubstitutedConstraintSatisfactionCache,
-    //     [&](UnsubstitutedConstraintSatisfactionCacheResult
-    //     &R) -> bool {
-    //       return Ctx.isAfterCheckpoint(static_cast<void
-    //       *>(R.SubstExpr.get()),
-    //                                    SlabCP)
-    //     });
-    assert(CP.CurrentCachedTemplateArgsSize ==
-           S.CurrentCachedTemplateArgs->size());
-  }
-
-  if (CP.UnsubstitutedConstraintSatisfactionCacheSize !=
-      S.UnsubstitutedConstraintSatisfactionCache.size()) {
-    llvm::dbgs() << "CP.UnsubstitutedConstraintSatisfactionCacheSize != "
-                    "S.UnsubstitutedConstraintSatisfactionCache.size()\n";
-    // eraseDenseMapIf(
-    //     S.UnsubstitutedConstraintSatisfactionCache,
-    //     [&](UnsubstitutedConstraintSatisfactionCacheResult &R) -> bool {
-    //       return Ctx.isAfterCheckpoint(static_cast<void
-    //       *>(R.SubstExpr.get()),
-    //                                    SlabCP)
-    //     });
-    assert(CP.UnsubstitutedConstraintSatisfactionCacheSize ==
-           S.UnsubstitutedConstraintSatisfactionCache.size());
-  }
-
-  if (CP.SubsumptionCacheSize != S.SubsumptionCache.size()) {
-    llvm::dbgs() << "CP.SubsumptionCacheSize != "
-                    "S.SubsumptionCache.size()\n";
-    // eraseDenseMapIf(
-    //     S.SubsumptionCache,
-    //     [&](llvm::DenseMap<std::pair<const NamedDecl *, const NamedDecl *>,
-    //                        bool>::key_value &KV) -> bool {
-    //       return Ctx.isAfterCheckpoint(static_cast<void *>(KV.first.first),
-    //                                    SlabCP) ||
-    //              Ctx.isAfterCheckpoint(static_cast<void *>(KV.first.second),
-    //                                    SlabCP);
-    //     });
-    assert(CP.SubsumptionCacheSize == S.SubsumptionCache.size());
-  }
-
-  if (CP.NormalizationCacheSize != S.NormalizationCache.size()) {
-    llvm::dbgs() << "CP.NormalizationCacheSize != "
-                    "S.NormalizationCache.size()\n";
-    //   llvm::DenseMap<ConstrainedDeclOrNestedRequirement, NormalizedConstraint
-    //   *> NormalizationCache;
-    assert(CP.NormalizationCacheSize == S.NormalizationCache.size());
-  }
-
-  if (CP.SatisfactionCacheSize != S.SatisfactionCache.size()) {
-    llvm::dbgs() << "CP.SatisfactionCacheSize != "
-                    "S.SatisfactionCache.size()\n";
-    //   llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &>
-    //   SatisfactionCache;
-    assert(CP.SatisfactionCacheSize == S.SatisfactionCache.size());
-  }
-
-  if (CP.SatisfactionStackSize != S.SatisfactionStack.size()) {
-    llvm::dbgs() << "CP.SatisfactionStackSize != "
-                    "S.SatisfactionStack.size()\n";
-    //         llvm::SmallVector<SatisfactionStackEntryTy, 10>
-    //         SatisfactionStack;
-    assert(CP.SatisfactionStackSize == S.SatisfactionStack.size());
-  }
-
-  // if (CP.NullabilityMapSize != S.NullabilityMap.size()) {
-  //   llvm::dbgs() << "CP.NullabilityMapSize != "
-  //                   "S.NullabilityMap.size()\n";
-  //   //  FileNullabilityMap NullabilityMap;;
-  //   assert(CP.NullabilityMapSize == S.NullabilityMap.size());
-  // }
-
-  if (CP.DeclsWithEffectsToVerifySize != S.DeclsWithEffectsToVerify.size()) {
-    llvm::dbgs() << "CP.DeclsWithEffectsToVerifySize != "
-                    "S.DeclsWithEffectsToVerify.size()\n";
-    //  SmallVector<const Decl *> DeclsWithEffectsToVerify;;
-    assert(CP.DeclsWithEffectsToVerifySize ==
-           S.DeclsWithEffectsToVerify.size());
-  }
-
-  if (CP.SpecialMemberCacheSize != S.SpecialMemberCache.size()) {
-    llvm::dbgs() << "CP.SpecialMemberCacheSize != "
-                    "S.SpecialMemberCache.size()\n";
-
-    eraseDenseMapIf(
-        S.SpecialMemberCache,
-        [&](llvm::detail::DenseMapPair<Sema::SpecialMemberCacheKey,
-                                       Sema::SpecialMemberOverloadResult> &KV)
-            -> bool {
-          return S.BumpAlloc.isAfterCheckpoint(
-              static_cast<void *>(KV.getSecond().getMethod()),
-              CP.SemaBumpSlabCP);
-        });
-    assert(CP.SpecialMemberCacheSize == S.SpecialMemberCache.size());
-  }
-
-  std::vector<NamedDecl *> ToRemoved;
-  for (auto *TmpD : S.getCurScope()->decls()) {
-    assert(TmpD && "This decl didn't get pushed??");
-
-    assert(isa<NamedDecl>(TmpD) && "Decl isn't NamedDecl?");
-    NamedDecl *D = cast<NamedDecl>(TmpD);
-
-    if (!D->getDeclName())
-      continue;
-
-    if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D), SlabCP)) {
-      if (D->getDeclName().getFETokenInfo())
-        S.IdResolver.RemoveDecl(D);
-      ToRemoved.push_back(D);
-    }
-  }
-
-  for (auto ND : ToRemoved)
-    S.getCurScope()->RemoveDecl(ND);
-
-  S.BumpAlloc.restoreToCheckPoint(CP.SemaBumpSlabCP);
-}
-
-} // namespace clang

>From 58155d0d14ba81b0b32261640f2c92f9ad8c0f7f Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 16 Sep 2026 12:28:19 +0530
Subject: [PATCH 05/13] Refactor code: remove spaces

---
 clang/include/clang/AST/ASTContext.h      | 1 -
 clang/include/clang/AST/DeclTemplate.h    | 1 -
 clang/lib/Interpreter/IncrementalParser.h | 3 ---
 3 files changed, 5 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index e3d09c8257d83..72e6bcf362aeb 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -240,7 +240,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
   friend class NestedNameSpecifier;
 
   mutable SmallVector<Type *, 0> Types;
-
   mutable llvm::FoldingSet<ExtQuals> ExtQualNodes;
   mutable llvm::UniquingSet<ComplexType> ComplexTypes;
   mutable llvm::UniquingSet<PointerType> PointerTypes{GeneralTypesLog2InitSize};
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index fd1c7df6e7813..d32b044ef40e2 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -825,7 +825,6 @@ class RedeclarableTemplateDecl : public TemplateDecl,
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
   friend class ASTReader;
-
   template <class decl_type> friend class RedeclarableTemplate;
 
   /// Retrieves the canonical declaration of this template.
diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h
index e6eaa7dddb7fd..b626cebaafcd7 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -63,9 +63,6 @@ class IncrementalParser {
 
   void CleanUpPTU(TranslationUnitDecl *MostRecentTU);
 
-  template <typename decl_type>
-  void RepairRedeclChain(decl_type *D, TranslationUnitDecl *PTU);
-
   /// Register a PTU produced by Parse.
   PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU,
                                       std::unique_ptr<llvm::Module> M = {});

>From cb80e9d3b67efbbfd393715609dd2c54c641335d Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Mon, 21 Sep 2026 14:32:39 +0530
Subject: [PATCH 06/13] Refactor code

---
 .../include/clang/Interpreter/ErrorRecovery.h | 300 ++++++++++--------
 1 file changed, 167 insertions(+), 133 deletions(-)

diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 2ef75b2492fa2..7421f28110955 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -17,9 +17,6 @@
 #include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
-#include "clang/Sema/Sema.h"
-
-#include "llvm/ADT/DenseMap.h"
 
 namespace clang {
 class ASTContext;
@@ -562,17 +559,13 @@ template <typename DataT> class StateAwareChain {
     return nullptr;
   }
 
-  /// Pure removal: drop every entry with ID >= \p ID (LIFO). Never
-  /// restores anything -- callers must read mostRecent()/getPrevious()
-  /// themselves first if they need the value about to be dropped.
+  /// removal: drop every entry with ID >= \p ID.
   void removeFrom(PTUID ID) {
     while (!History.empty() && History.back().ID >= ID)
       History.pop_back(); // no delete.
   }
 };
 
-// struct UnusedTrait {};
-
 using RecordDeclDefinitionDataChain = StateAwareChain<DefinitionDataFootprint>;
 using SpecializationChain = StateAwareChain<SpecializationFootprint>;
 using VarSpecializationChain = StateAwareChain<VarSpecializationFootprint>;
@@ -630,6 +623,7 @@ class PTUCheckpointLedger {
   }
 
   bool isFromThisPTU(const ASTContext &Ctx, const void *Ptr, PTUID ID) const {
+    assert(ID < CheckpointBeforePTU.size());
     return Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
   }
 
@@ -701,6 +695,22 @@ template <typename OwnerT, typename ValueT> class FieldMutationChain {
       Log.erase(O);
   }
 
+  /// Same trim-then-erase-if-empty as removeFrom(ID), scoped to one owner
+  /// instead of sweeping the whole Log -- for callers that already know
+  /// exactly which owners this PTU touched (see PTUStateInfo's
+  /// TouchedFunctionTypeOwners/TouchedTagdeclOwners) and don't need to
+  /// rediscover that by walking every owner ever logged.
+  void removeFromOwner(const OwnerT *Owner, PTUID ID) {
+    auto It = Log.find(Owner);
+    if (It == Log.end())
+      return;
+    auto &Entries = It->second;
+    while (!Entries.empty() && Entries.back().ID >= ID)
+      Entries.pop_back();
+    if (Entries.empty())
+      Log.erase(It);
+  }
+
   void forget(const OwnerT *Owner) { Log.erase(Owner); }
 };
 
@@ -763,19 +773,39 @@ struct PTUStateInfo {
   /// here touched info mean other this belongs to other PTUs;
   llvm::SmallPtrSet<const DeclContext *, 4> TouchedDC;
 
+  /// Exactly the decls this PTU's own commit() pushed a footprint-chain
+  /// entry for (DefinitionData/Specialization/MemberSpecialization, via
+  /// chainFor()/memberSpecChainFor()), keyed on the canonical decl the
+  /// same way those accessors are. Lets undo walk only what this PTU
+  /// actually touched instead of sweeping every decl ever tracked across
+  /// every PTU -- the exact opposite of commit(), scoped the same way
+  /// commit() itself is scoped.
+  llvm::SmallPtrSet<const Decl *, 8> TouchedFootprintDecls;
+
+  /// Same idea for the two FieldMutationChain logs (FunctionTypeMutations,
+  /// TagdeclInfos), which are keyed by owner rather than living in
+  /// LinkedDecls -- kept as two separate sets since the owner types
+  /// (FunctionDecl vs TagDecl) differ.
+  llvm::SmallPtrSet<const FunctionDecl *, 4> TouchedFunctionTypeOwners;
+  llvm::SmallPtrSet<const TagDecl *, 4> TouchedTagdeclOwners;
+
+  explicit PTUStateInfo(PTUID ID) : ID(ID) {}
+
   template <typename KindT>
   void noteMutated(const Decl *D, DeclShape S, KindT K) {
     if (!D->isDefinedOutsideFunctionOrMethod())
       return;
     auto [It, Inserted] = Mutations.try_emplace(D);
     if (Inserted) {
-      assert(It->second.S == S && "same decl noted under two different shapes");
       It->second.S = S;
     }
+
+    assert(It->second.S == S && "same decl noted under two different shapes");
+
     It->second.add(K);
   }
 
-  void verifyMutations();
+  void verifyMutations(PTUMutationActions &Actions);
 
   // False until this PTU is actually committed.
   // PTUMutationActions uses this to decide what "undo" should do:
@@ -822,11 +852,10 @@ class SweepTracker {
       Active.erase(It);
   }
 
-  // Call at commit(ID) time. OnConfirmed is invoked as
+  // Call at commit(ID) time. PTUMutationActions is invoked as
   // (const Decl *D, DeclShape S, uint32_t ConfirmedKinds) for every decl
   // that had something newly confirmed this sweep.
-  template <typename OnConfirmedFn>
-  void sweep(PTUID ID, OnConfirmedFn &&OnConfirmed);
+  void sweep(PTUMutationActions &Act);
 };
 
 //===----------------------------------------------------------------------===//
@@ -1028,10 +1057,9 @@ class IncrementalStateTracker {
   ASTContext &Ctx;
   LangMode Mode;
   PTUID NextID = 0;
-  PTUID CurID = NextID;
   PTUCheckpointLedger PTUSlabCheckpoints;
 
-  mutable llvm::DenseMap<PTUID, PTUStateInfo> PTUStateInfos;
+  mutable std::vector<PTUStateInfo> PTUStack;
 
   // Stores all declaration footprint state in one map instead of maintaining
   // separate maps for each declaration/specialization kind. The generator
@@ -1048,77 +1076,88 @@ class IncrementalStateTracker {
 
   friend class PTUMutationActions;
 
-  SweepTracker &getHiddenMutationTracker() { return HiddenMutationTracker; }
+  void noteFunctionTypeMutation(PTUID ID, const FunctionDecl *Owner,
+                                QualType OldValue) {
+    FunctionTypeMutations.noteMutation(ID, Owner, OldValue);
+    current().TouchedFunctionTypeOwners.insert(Owner);
+  }
+  void noteTagDeclTypeMutation(PTUID ID, const TagDecl *Owner,
+                               const Type *OldValue) {
+    TagdeclInfos.noteMutation(ID, Owner, OldValue);
+    current().TouchedTagdeclOwners.insert(Owner);
+  }
 
   /// Get-or-create the CXXClassDeclNode backing Canon, acquiring one from
   /// the generator on first use. The only place a CXXClassDeclNode is
   /// created for this map.
-  CXXClassDeclNode &recordNodeFor(const CXXRecordDecl *Canon) const {
-    DeclLinkedState &Info = LinkedDecls[Canon];
+  template <typename NodeT, typename AcquireFn>
+  NodeT &nodeFor(const Decl *D, AcquireFn Acquire) {
+    current().TouchedFootprintDecls.insert(D);
+    DeclLinkedState &Info = LinkedDecls[D];
     if (Info.isNull())
-      Info = Generator.acquireRecordNode();
-    return *cast<CXXClassDeclNode *>(Info);
+      Info = (Generator.*Acquire)();
+    return *cast<NodeT *>(Info);
   }
-  VarDeclNode &varNodeFor(const VarDecl *Canon) const {
-    DeclLinkedState &Info = LinkedDecls[Canon];
-    if (Info.isNull())
-      Info = Generator.acquireVarNode();
-    return *cast<VarDeclNode *>(Info);
+
+  template <typename NodeT> NodeT *lookupNode(const Decl *D) const {
+    auto It = LinkedDecls.find(D);
+    if (It == LinkedDecls.end() || It->second.isNull())
+      return nullptr;
+    return dyn_cast<NodeT *>(It->second);
   }
-  FunctionDeclNode &functionNodeFor(const FunctionDecl *Canon) const {
-    DeclLinkedState &Info = LinkedDecls[Canon];
-    if (Info.isNull())
-      Info = Generator.acquireFunctionNode();
-    return *cast<FunctionDeclNode *>(Info);
+
+  CXXClassDeclNode &recordNodeFor(const CXXRecordDecl *RD) {
+    return nodeFor<CXXClassDeclNode>(
+        RD, &LinkedDeclNodeGenerator::acquireRecordNode);
   }
-  EnumDeclNode &enumNodeFor(const EnumDecl *Canon) const {
-    DeclLinkedState &Info = LinkedDecls[Canon];
-    if (Info.isNull())
-      Info = Generator.acquireEnumNode();
-    return *cast<EnumDeclNode *>(Info);
+  VarDeclNode &varNodeFor(const VarDecl *VD) {
+    return nodeFor<VarDeclNode>(VD, &LinkedDeclNodeGenerator::acquireVarNode);
+  }
+  FunctionDeclNode &functionNodeFor(const FunctionDecl *FD) {
+    return nodeFor<FunctionDeclNode>(
+        FD, &LinkedDeclNodeGenerator::acquireFunctionNode);
+  }
+  EnumDeclNode &enumNodeFor(const EnumDecl *ED) {
+    return nodeFor<EnumDeclNode>(ED, &LinkedDeclNodeGenerator::acquireEnumNode);
+  }
+
+  template <typename WantT, typename NodeT, typename AcquireFn>
+  WantT &specChain(NodeT &Node, AcquireFn Acquire) {
+    if (auto *C = Node.Spec.template dyn_cast<WantT *>())
+      return *C;
+    assert(Node.Spec.isNull() && "node already holds the other Spec kind");
+    auto *C = (Generator.*Acquire)();
+    Node.Spec = C;
+    return *C;
   }
 
   RecordDeclDefinitionDataChain &chainFor(const CXXRecordDecl *RD) {
-    CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+    CXXClassDeclNode &Node = recordNodeFor(RD);
     if (!Node.DefData)
       Node.DefData = Generator.acquireDefDataChain();
     return *Node.DefData;
   }
 
-  SpecializationChain &chainFor(const ClassTemplateSpecializationDecl *Spec) {
-    CXXClassDeclNode &Node = recordNodeFor(
-        cast<ClassTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
-    auto *Chain = Node.Spec.dyn_cast<SpecializationChain *>();
-    if (!Chain) {
-      Chain = Generator.acquireClassSpecChain();
-      Node.Spec = Chain;
-    }
-    return *Chain;
+  SpecializationChain &chainFor(const ClassTemplateSpecializationDecl *S) {
+    return specChain<SpecializationChain>(
+        recordNodeFor(cast<CXXRecordDecl>(S)),
+        &LinkedDeclNodeGenerator::acquireClassSpecChain);
   }
 
-  VarSpecializationChain &chainFor(const VarTemplateSpecializationDecl *Spec) {
-    VarDeclNode &Node = varNodeFor(
-        cast<VarTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
-    auto *Chain = Node.Spec.dyn_cast<VarSpecializationChain *>();
-    if (!Chain) {
-      Chain = Generator.acquireVarSpecChain();
-      Node.Spec = Chain;
-    }
-    return *Chain;
+  VarSpecializationChain &chainFor(const VarTemplateSpecializationDecl *S) {
+    return specChain<VarSpecializationChain>(
+        varNodeFor(cast<VarDecl>(S)),
+        &LinkedDeclNodeGenerator::acquireVarSpecChain);
   }
 
-  FunctionSpecializationChain &chainFor(const FunctionDecl *FD) {
-    FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
-    auto *Chain = Node.Spec.dyn_cast<FunctionSpecializationChain *>();
-    if (!Chain) {
-      Chain = Generator.acquireFunctionSpecChain();
-      Node.Spec = Chain;
-    }
-    return *Chain;
+  FunctionSpecializationChain &chainFor(const FunctionDecl *S) {
+    return specChain<FunctionSpecializationChain>(
+        functionNodeFor(cast<FunctionDecl>(S)),
+        &LinkedDeclNodeGenerator::acquireFunctionSpecChain);
   }
 
   MemberSpecializationChain &memberSpecChainFor(const FunctionDecl *FD) {
-    FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
+    FunctionDeclNode &Node = functionNodeFor(FD);
     auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
     if (!Chain) {
       Chain = Generator.acquireMemberSpecChain();
@@ -1127,7 +1166,7 @@ class IncrementalStateTracker {
     return *Chain;
   }
   MemberSpecializationChain &memberSpecChainFor(const VarDecl *VD) {
-    VarDeclNode &Node = varNodeFor(VD->getCanonicalDecl());
+    VarDeclNode &Node = varNodeFor(VD);
     auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
     if (!Chain) {
       Chain = Generator.acquireMemberSpecChain();
@@ -1136,7 +1175,7 @@ class IncrementalStateTracker {
     return *Chain;
   }
   MemberSpecializationChain &memberSpecChainFor(const CXXRecordDecl *RD) {
-    CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+    CXXClassDeclNode &Node = recordNodeFor(RD);
     auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
     if (!Chain) {
       Chain = Generator.acquireMemberSpecChain();
@@ -1145,86 +1184,103 @@ class IncrementalStateTracker {
     return *Chain;
   }
   MemberSpecializationChain &memberSpecChainFor(const EnumDecl *ED) {
-    EnumDeclNode &Node = enumNodeFor(ED->getCanonicalDecl());
+    EnumDeclNode &Node = enumNodeFor(ED);
     if (!Node.MemberSpec)
       Node.MemberSpec = Generator.acquireMemberSpecChain();
     return *Node.MemberSpec;
   }
 
-  RecordDeclDefinitionDataChain *getChainFor(const CXXRecordDecl *RD) const {
-    CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
-    return Node.DefData;
+  const RecordDeclDefinitionDataChain *
+  getChainFor(const CXXRecordDecl *RD) const {
+    CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(RD);
+    return N ? N->DefData : nullptr;
   }
-
-  SpecializationChain *
-  getChainFor(const ClassTemplateSpecializationDecl *Spec) const {
-    CXXClassDeclNode &Node = recordNodeFor(
-        cast<ClassTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
-    return Node.Spec.dyn_cast<SpecializationChain *>();
+  const SpecializationChain *
+  getChainFor(const ClassTemplateSpecializationDecl *S) const {
+    CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(S);
+    return N ? N->Spec.dyn_cast<SpecializationChain *>() : nullptr;
   }
 
-  VarSpecializationChain *
+  const VarSpecializationChain *
   getChainFor(const VarTemplateSpecializationDecl *Spec) const {
-    VarDeclNode &Node = varNodeFor(
-        cast<VarTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
-    return Node.Spec.dyn_cast<VarSpecializationChain *>();
+    VarDeclNode *N = lookupNode<VarDeclNode>(Spec);
+    return N ? N->Spec.dyn_cast<VarSpecializationChain *>() : nullptr;
   }
 
-  FunctionSpecializationChain *getChainFor(const FunctionDecl *FD) const {
-    FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
-    return Node.Spec.dyn_cast<FunctionSpecializationChain *>();
+  const FunctionSpecializationChain *
+  getChainFor(const FunctionDecl *Spec) const {
+    FunctionDeclNode *N = lookupNode<FunctionDeclNode>(Spec);
+    return N ? N->Spec.dyn_cast<FunctionSpecializationChain *>() : nullptr;
   }
 
-  MemberSpecializationChain *
+  const MemberSpecializationChain *
   getMemberSpecChainFor(const FunctionDecl *FD) const {
-    FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
-    return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+    FunctionDeclNode *N = lookupNode<FunctionDeclNode>(FD);
+    return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
   }
-  MemberSpecializationChain *getMemberSpecChainFor(const VarDecl *VD) const {
-    VarDeclNode &Node = varNodeFor(VD->getCanonicalDecl());
-    return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+
+  const MemberSpecializationChain *
+  getMemberSpecChainFor(const VarDecl *VD) const {
+    VarDeclNode *N = lookupNode<VarDeclNode>(VD);
+    return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
   }
-  MemberSpecializationChain *
+
+  const MemberSpecializationChain *
   getMemberSpecChainFor(const CXXRecordDecl *RD) const {
-    CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
-    return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+    CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(RD);
+    return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
   }
-  MemberSpecializationChain *getMemberSpecChainFor(const EnumDecl *ED) const {
-    EnumDeclNode &Node = enumNodeFor(ED->getCanonicalDecl());
-    return Node.MemberSpec;
+
+  const MemberSpecializationChain *
+  getMemberSpecChainFor(const EnumDecl *ED) const {
+    EnumDeclNode *N = lookupNode<EnumDeclNode>(ED);
+    return N ? N->MemberSpec : nullptr;
   }
 
 public:
   IncrementalStateTracker(ASTContext &Ctx, LangMode M) : Ctx(Ctx), Mode(M) {}
 
-  void beginPTU(llvm::SlabCheckPoint CP) {
-    CurID = NextID;
-    PTUSlabCheckpoints.recordCheckpoint(CurID, CP);
-    PTUStateInfos.try_emplace(CurID, PTUStateInfo{CurID});
-    ++NextID;
+  LangMode getMode() const { return Mode; }
+
+  PTUStateInfo &current() const {
+    assert(!PTUStack.empty() && "no PTU has been started");
+    return PTUStack.back();
   }
 
-  PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
+  PTUID currentID() const { return current().ID; }
+  bool hasOpenPTU() const { return !PTUStack.empty(); }
 
-  bool isFromThisPTU(const void *Ptr, PTUID ID) {
-    return PTUSlabCheckpoints.isFromThisPTU(Ctx, Ptr, ID);
+  void beginPTU(llvm::SlabCheckPoint CP) {
+    PTUID ID = NextID++;
+    PTUSlabCheckpoints.recordCheckpoint(ID, CP);
+    PTUStack.emplace_back(ID);
   }
 
-  PTUStateInfo &current() const {
-    assert(NextID > 0 && "no PTU has been started");
-    auto It = PTUStateInfos.find(CurID);
-    assert(It != PTUStateInfos.end());
-    return It->second;
+  bool isFromThisPTU(const void *Ptr, PTUID ID) const {
+    return PTUSlabCheckpoints.isFromThisPTU(Ctx, Ptr, ID);
+  }
+  bool isFromCurrentPTU(const void *Ptr) const {
+    return isFromThisPTU(Ptr, currentID());
   }
 
+  SweepTracker &getHiddenMutationTracker() { return HiddenMutationTracker; }
+  PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
+
   void undoLastEntries();
 
 private:
+  void popCurrentPTU() {
+    PTUID ID = currentID();
+    PTUStack.pop_back();
+    PTUSlabCheckpoints.undoFrom(ID);
+    NextID = ID;
+  }
+
   template <typename ChainT>
-  static bool rollbackChainField(ChainT *&Field, PTUID ID,
-                                 LinkedDeclNodeGenerator &Gen);
+  static bool removeChainField(ChainT *&Field, PTUID ID,
+                               LinkedDeclNodeGenerator &Gen);
 
-  static bool rollbackRecordSpec(
+  static bool removeRecordSpec(
       llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *>
           &Spec,
       PTUID ID, LinkedDeclNodeGenerator &Gen);
@@ -1285,34 +1341,12 @@ class PTUMutationActions {
   void commitEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec,
                         bool IsNew);
 
-  // static DeclShape classifyShape(const Decl *D);
-
-  // class DeclStateCommitProxy;
-
-  // class DeclStateRestoreProxy;
-
   template <typename DeclStateProxyT>
   void walkDecls(const DeclContext *DC, DeclStateProxyT &Proxy);
 
   // static uint32_t classifyPossibleKinds(DeclShape S, const Decl *D);
 
-  /// Given a Decl already known to be DeclShape S with FlaggedKinds set
-  /// (more than one bit at once is the normal case, not an edge case --
-  /// a single FunctionDecl can be MSI-backed AND have an unresolved
-  /// exception spec at the same time), returns the subset of
-  /// FlaggedKinds that actually differ from the last recorded snapshot.
-  /// Real chain access, not a guess: builds a fresh footprint the same
-  /// way commit() does and compares it against chainFor/
-  /// memberSpecChainFor's mostRecent() via the same operator== every
-  /// StateAwareChain already uses for its own dedup. A bit with no
-  /// chain anywhere in this file (DeducedReturnType has none -- see its
-  /// own listener override's comment; TypeForDeclChanged/
-  /// EvaluatedValueCached likewise) is never returned as verified --
-  /// there is nothing to compare it against, so it cannot be confirmed,
-  /// full stop, not assumed either way.
-  // static uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t
-  // FlaggedKinds,
-  //                            PTUID ID);
+  uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
 
   void commitLevel1(PTUID ID, const Decl *D, MutationRecord &Rec,
                     bool IsNew = false);

>From 197a9d7fcc86b302f153ca618c55971890b09407 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Thu, 24 Sep 2026 16:08:07 +0530
Subject: [PATCH 07/13] Add initial ErrRecovery implementation and refactor
 code

---
 .../include/clang/Interpreter/ErrorRecovery.h |  511 +++----
 clang/lib/Interpreter/CMakeLists.txt          |    1 +
 clang/lib/Interpreter/ErrRecovery.cpp         | 1321 +++++++++++++++++
 3 files changed, 1520 insertions(+), 313 deletions(-)
 create mode 100644 clang/lib/Interpreter/ErrRecovery.cpp

diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 7421f28110955..61380ec6ea0ad 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -20,6 +20,7 @@
 
 namespace clang {
 class ASTContext;
+class Sema;
 
 /// Index of a per PTU State.
 using PTUID = unsigned;
@@ -35,102 +36,19 @@ using PTUID = unsigned;
 // This footprint exists purely to undo the effects of implicit special
 // member generation.
 struct DefinitionDataFootprint {
-  unsigned Aggregate : 1;
-  unsigned PlainOldData : 1;
-  unsigned Empty : 1;
-  unsigned Polymorphic : 1;
-  unsigned IsStandardLayout : 1;
-  unsigned IsCXX11StandardLayout : 1;
-  unsigned HasTrivialSpecialMembers : 6;
-  unsigned HasTrivialSpecialMembersForCall : 6;
-  unsigned DeclaredNonTrivialSpecialMembers : 6;
-  unsigned DeclaredNonTrivialSpecialMembersForCall : 6;
-  unsigned HasIrrelevantDestructor : 1;
-  unsigned HasConstexprNonCopyMoveConstructor : 1;
-  unsigned HasDefaultedDefaultConstructor : 1;
-  unsigned HasConstexprDefaultConstructor : 1;
-  unsigned HasDeclaredCopyConstructorWithConstParam : 1;
-  unsigned HasDeclaredCopyAssignmentWithConstParam : 1;
-  unsigned IsAnyDestructorNoReturn : 1;
-  unsigned DeclaredSpecialMembers : 6;
+#define FIELD(Name, Width, Merge) unsigned Name : Width;
+#include "clang/AST/CXXRecordDeclDefinitionBits.def"
 
   bool operator==(const DefinitionDataFootprint &O) const {
-    return Aggregate == O.Aggregate && PlainOldData == O.PlainOldData &&
-           Empty == O.Empty && Polymorphic == O.Polymorphic &&
-           IsStandardLayout == O.IsStandardLayout &&
-           IsCXX11StandardLayout == O.IsCXX11StandardLayout &&
-           HasTrivialSpecialMembers == O.HasTrivialSpecialMembers &&
-           HasTrivialSpecialMembersForCall ==
-               O.HasTrivialSpecialMembersForCall &&
-           DeclaredNonTrivialSpecialMembers ==
-               O.DeclaredNonTrivialSpecialMembers &&
-           DeclaredNonTrivialSpecialMembersForCall ==
-               O.DeclaredNonTrivialSpecialMembersForCall &&
-           HasIrrelevantDestructor == O.HasIrrelevantDestructor &&
-           HasConstexprNonCopyMoveConstructor ==
-               O.HasConstexprNonCopyMoveConstructor &&
-           HasDefaultedDefaultConstructor == O.HasDefaultedDefaultConstructor &&
-           HasConstexprDefaultConstructor == O.HasConstexprDefaultConstructor &&
-           HasDeclaredCopyConstructorWithConstParam ==
-               O.HasDeclaredCopyConstructorWithConstParam &&
-           HasDeclaredCopyAssignmentWithConstParam ==
-               O.HasDeclaredCopyAssignmentWithConstParam &&
-           IsAnyDestructorNoReturn == O.IsAnyDestructorNoReturn &&
-           DeclaredSpecialMembers == O.DeclaredSpecialMembers;
+#define FIELD(Name, Width, Merge)                                              \
+  if (Name != O.Name)                                                          \
+    return false;
+#include "clang/AST/CXXRecordDeclDefinitionBits.def"
+    return true;
   }
   bool operator!=(const DefinitionDataFootprint &O) const {
     return !(*this == O);
   }
-
-  // void update(const CXXRecordDecl::DefinitionData &Live) {
-  //   Aggregate = Live.Aggregate;
-  //   PlainOldData = Live.PlainOldData;
-  //   Empty = Live.Empty;
-  //   Polymorphic = Live.Polymorphic;
-  //   IsStandardLayout = Live.IsStandardLayout;
-  //   IsCXX11StandardLayout = Live.IsCXX11StandardLayout;
-  //   HasTrivialSpecialMembers = Live.HasTrivialSpecialMembers;
-  //   HasTrivialSpecialMembersForCall = Live.HasTrivialSpecialMembersForCall;
-  //   DeclaredNonTrivialSpecialMembers = Live.DeclaredNonTrivialSpecialMembers;
-  //   DeclaredNonTrivialSpecialMembersForCall =
-  //       Live.DeclaredNonTrivialSpecialMembersForCall;
-  //   HasIrrelevantDestructor = Live.HasIrrelevantDestructor;
-  //   HasConstexprNonCopyMoveConstructor =
-  //       Live.HasConstexprNonCopyMoveConstructor;
-  //   HasDefaultedDefaultConstructor = Live.HasDefaultedDefaultConstructor;
-  //   HasConstexprDefaultConstructor = Live.HasConstexprDefaultConstructor;
-  //   HasDeclaredCopyConstructorWithConstParam =
-  //       Live.HasDeclaredCopyConstructorWithConstParam;
-  //   HasDeclaredCopyAssignmentWithConstParam =
-  //       Live.HasDeclaredCopyAssignmentWithConstParam;
-  //   IsAnyDestructorNoReturn = Live.IsAnyDestructorNoReturn;
-  //   DeclaredSpecialMembers = Live.DeclaredSpecialMembers;
-  // }
-
-  // void restore(CXXRecordDecl::DefinitionData &Live) const {
-  //   Live.Aggregate = Aggregate;
-  //   Live.PlainOldData = PlainOldData;
-  //   Live.Empty = Empty;
-  //   Live.Polymorphic = Polymorphic;
-  //   Live.IsStandardLayout = IsStandardLayout;
-  //   Live.IsCXX11StandardLayout = IsCXX11StandardLayout;
-  //   Live.HasTrivialSpecialMembers = HasTrivialSpecialMembers;
-  //   Live.HasTrivialSpecialMembersForCall = HasTrivialSpecialMembersForCall;
-  //   Live.DeclaredNonTrivialSpecialMembers = DeclaredNonTrivialSpecialMembers;
-  //   Live.DeclaredNonTrivialSpecialMembersForCall =
-  //       DeclaredNonTrivialSpecialMembersForCall;
-  //   Live.HasIrrelevantDestructor = HasIrrelevantDestructor;
-  //   Live.HasConstexprNonCopyMoveConstructor =
-  //       HasConstexprNonCopyMoveConstructor;
-  //   Live.HasDefaultedDefaultConstructor = HasDefaultedDefaultConstructor;
-  //   Live.HasConstexprDefaultConstructor = HasConstexprDefaultConstructor;
-  //   Live.HasDeclaredCopyConstructorWithConstParam =
-  //       HasDeclaredCopyConstructorWithConstParam;
-  //   Live.HasDeclaredCopyAssignmentWithConstParam =
-  //       HasDeclaredCopyAssignmentWithConstParam;
-  //   Live.IsAnyDestructorNoReturn = IsAnyDestructorNoReturn;
-  //   Live.DeclaredSpecialMembers = DeclaredSpecialMembers;
-  // }
 };
 
 // Stores the state of a class template specialization. Tracks its
@@ -234,17 +152,15 @@ struct FunctionSpecializationFootprint {
 };
 
 // Stores the state of an ordinary class member created from a class template
-// instantiation. Tracks its specialization kind, point of instantiation, and
-// source location so the state can be compared and restored.
+// instantiation. Tracks its specialization kind and point of instantiation
+// so the state can be compared and restored.
 struct MemberSpecializationFootprint {
   unsigned SpecializationKind : 3;
   SourceLocation PointOfInstantiation;
-  SourceLocation Location;
 
   bool operator==(const MemberSpecializationFootprint &O) const {
     return SpecializationKind == O.SpecializationKind &&
-           PointOfInstantiation == O.PointOfInstantiation &&
-           Location == O.Location;
+           PointOfInstantiation == O.PointOfInstantiation;
   }
   bool operator!=(const MemberSpecializationFootprint &O) const {
     return !(*this == O);
@@ -254,7 +170,6 @@ struct MemberSpecializationFootprint {
     const MemberSpecializationInfo *MSI = Live.getMemberSpecializationInfo();
     SpecializationKind = MSI->getTemplateSpecializationKind();
     PointOfInstantiation = MSI->getPointOfInstantiation();
-    Location = Live.getLocation();
   }
 
   template <typename OwnerT> void restore(OwnerT &Live) const {
@@ -262,10 +177,12 @@ struct MemberSpecializationFootprint {
     MSI->setTemplateSpecializationKind(
         static_cast<TemplateSpecializationKind>(SpecializationKind));
     MSI->setPointOfInstantiation(PointOfInstantiation);
-    Live.setLocation(Location);
   }
 };
 
+class PTUMutationActions;
+class PTUCheckpointLedger;
+
 class DeclStateReverter {
 private:
   struct TemplateCommonAccess : RedeclarableTemplateDecl {
@@ -286,6 +203,16 @@ class DeclStateReverter {
     using ClassTemplateDecl::getCommonPtr;
   };
 
+  struct ClassTemplateSpecAccess : ClassTemplateDecl {
+    using ClassTemplateDecl::getSpecializations;
+  };
+  struct FunctionTemplateSpecAccess : FunctionTemplateDecl {
+    using FunctionTemplateDecl::getSpecializations;
+  };
+  struct VarTemplateSpecAccess : VarTemplateDecl {
+    using VarTemplateDecl::getSpecializations;
+  };
+
   static bool canonInjectedTSTValid(const ClassTemplateDecl &CTD) {
     auto *Ptr =
         static_cast<const ClassTemplateCommonAccess &>(CTD).getCommonPtr();
@@ -436,14 +363,13 @@ class DeclStateReverter {
 
   // True if Common could still be created later -- i.e. nobody has called
   // getCommonPtr() anywhere in this template's redecl chain yet.
-  static bool needToTrackCommonPtr(const RedeclarableTemplateDecl &RT) {
+  static bool isCommonPtrValid(const RedeclarableTemplateDecl &RT) {
     return !rawCommonPtr(RT);
   }
 
   // True if CanonInjectedTST could still be cached later. Only valid to
   // call once Common itself is confirmed to exist (see needToTrackCommonPtr).
-  static bool
-  needToTrackTemplateCanonInjectedTST(const ClassTemplateDecl *CTD) {
+  static bool isTemplateCanonInjectedTSTValid(const ClassTemplateDecl *CTD) {
     return !canonInjectedTSTValid(*CTD);
   }
 
@@ -458,6 +384,19 @@ class DeclStateReverter {
     clearCanonInjectedTST(CTD);
   }
 
+  // Specializations are appended in commit order, and rollback is strictly
+  // LIFO (only the most recently committed PTU is ever rolled back). So all
+  // specializations added by a given PTU form a trailing run at the back of
+  // the FoldingSetVector. Popping entries while the back entry belongs to
+  // this PTU is therefore sufficient.
+  //
+  // FoldingSetVector also has no arbitrary-position erase – only
+  // pop_back()/clear() – which is another reason to walk from the back
+  // rather than filter the entries in place.
+  static void removeSpecializations(PTUCheckpointLedger &Ledger,
+                                    const RedeclarableTemplateDecl *TD,
+                                    PTUID ID);
+
   // static const Type *getRawTypeForDecl(const TypeDecl *TD) {
   //   return TD->TypeForDecl;
   // }
@@ -470,40 +409,36 @@ class DeclStateReverter {
   // static void resetTypeForDecl(TypeDecl *TD) { TD->TypeForDecl = nullptr; }
 
 private:
-  ASTContext &Ctx;
-  llvm::SlabCheckPoint SlabCP;
-  llvm::SmallPtrSet<const DeclContext *, 8> RepairedLexicalContexts;
-
-  bool isAfterCP(const void *P) const {
-    return Ctx.getAllocator().isAfterCheckpoint(P, SlabCP);
-  }
+  PTUCheckpointLedger &PTUSlabCheckpoints;
 
 public:
-  DeclStateReverter(ASTContext &Ctx, llvm::SlabCheckPoint CP)
-      : Ctx(Ctx), SlabCP(CP) {}
+  friend PTUMutationActions;
+
+  explicit DeclStateReverter(PTUCheckpointLedger &Ledger)
+      : PTUSlabCheckpoints(Ledger) {}
 
   static bool isExtensibleContainer(const Decl *D) {
     return isa<NamespaceDecl>(D) || isa<CXXRecordDecl>(D);
   }
 
-  static bool isRedeclarableOrOnlyDecl(Decl *D) {
-    return D->getPreviousDecl() != nullptr;
+  static bool hasNoPreviousDecl(Decl *D) {
+    return D->getPreviousDecl() == nullptr;
   }
 
   void detachDefData(const Decl *D);
 
-  void detachCommonBase(const Decl *D);
+  static void detachCommonBase(const RedeclarableTemplateDecl *RT);
 
   /// Remove D from its semantic context's lookup map, reinstating the
   /// previous declaration if D had replaced one in-place (which is what
   /// StoredDeclsList::HandleRedeclaration does on a redeclaration --
   /// erasing the slot outright would lose the older decl entirely; that
   /// is the ReopenNs failure).
-  void detachFromDCLookup(const Decl *D);
+  void detachFromDCLookup(Decl *D, PTUID ID);
 
   // Walk D's redecl chain looking for the newest decl that predates this
   // PTU. Returns nullptr if the entire chain was created this PTU.
-  template <typename DeclT> DeclT *findSurvivor(DeclT *D) const;
+  template <typename DeclT> DeclT *findSurvivor(DeclT *D, PTUID ID) const;
 
   template <typename decl_type>
   void patchRedeclLink(Redeclarable<decl_type> *D, decl_type *Survivor) {
@@ -512,12 +447,12 @@ class DeclStateReverter {
 
   /// Point the canonical decl's "most recent" link back at the newest
   /// redeclaration that predates this PTU.
-  void detachFromRedeclChain(const Decl *D);
+  void detachFromRedeclChain(const Decl *D, PTUID ID);
 
-  void repairLexicalChain(DeclContext &DC);
+  void repairLexicalChain(DeclContext &DC, PTUID ID);
 
 private:
-  NamedDecl *tryDetachRedeclChain(Decl *D);
+  NamedDecl *tryDetachRedeclChain(Decl *D, PTUID ID);
 };
 
 template <typename DataT> struct Snapshot {
@@ -552,12 +487,12 @@ template <typename DataT> class StateAwareChain {
     History.push_back(Snapshot<DataT>{ID, Fresh});
   }
 
-  const DataT *getPrevious(PTUID ID) const {
-    for (auto It = History.rbegin(); It != History.rend(); ++It)
-      if (It->ID < ID)
-        return It->Data;
-    return nullptr;
-  }
+  // const DataT *getPrevious(PTUID ID) const {
+  //   for (auto It = History.rbegin(); It != History.rend(); ++It)
+  //     if (It->ID < ID)
+  //       return It->Data;
+  //   return nullptr;
+  // }
 
   /// removal: drop every entry with ID >= \p ID.
   void removeFrom(PTUID ID) {
@@ -574,10 +509,24 @@ using FunctionSpecializationChain =
 using MemberSpecializationChain =
     StateAwareChain<MemberSpecializationFootprint>;
 
+/// Maps addresses to the PTU that allocated them, by keeping the slab
+/// checkpoint taken before each PTU began.
+///
+/// PTU IDs are the vector index, which is what makes the lookup a binary
+/// search: checkpoints are monotonically increasing, so "is this address
+/// after checkpoint N" is true for all N up to the allocating PTU and
+/// false after.
 class PTUCheckpointLedger {
+  const ASTContext &Ctx;
   llvm::SmallVector<llvm::SlabCheckPoint, 16> CheckpointBeforePTU;
 
+  bool isAfter(const void *Ptr, PTUID ID) const {
+    return Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
+  }
+
 public:
+  explicit PTUCheckpointLedger(const ASTContext &Ctx) : Ctx(Ctx) {}
+
   /// Called once, right before parsing PTU \p ID begins.
   void recordCheckpoint(PTUID ID, llvm::SlabCheckPoint CP) {
     assert(ID == CheckpointBeforePTU.size() &&
@@ -585,49 +534,30 @@ class PTUCheckpointLedger {
     CheckpointBeforePTU.push_back(CP);
   }
 
-  /// \return the PTU that allocated \p Ptr, or std::nullopt if \p Ptr
-  /// predates the oldest recorded checkpoint.
-  ///
-  /// Walks newest-to-oldest: checkpoints only ever move forward for state
-  /// that has survived (committed PTUs are never rewound), so the first
-  /// checkpoint for which \p Ptr is "after" is the PTU that produced it.
-  std::optional<PTUID> attribute(const ASTContext &Ctx, const void *Ptr) const {
-    for (PTUID ID = CheckpointBeforePTU.size(); ID-- > 0;) {
-      if (Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]))
-        return ID;
-    }
-    return std::nullopt;
+  /// Was \p Ptr allocated during PTU \p ID or later?
+  bool isFromThisPTU(const void *Ptr, PTUID ID) const {
+    assert(ID < CheckpointBeforePTU.size());
+    return isAfter(Ptr, ID);
   }
 
-  inline std::optional<PTUID> attributeByAddress(const ASTContext &Ctx,
-                                                 const void *Ptr) {
-    // Binary search for the largest ID whose checkpoint Ptr is after -- i.e.
-    // the newest PTU boundary this address was allocated on or past.
+  /// The PTU that allocated \p Ptr, or nullopt if it predates the oldest
+  /// recorded checkpoint. The newest ID whose checkpoint \p Ptr is after.
+  std::optional<PTUID> attribute(const void *Ptr) const {
     size_t Lo = 0, Hi = CheckpointBeforePTU.size();
     std::optional<PTUID> Result;
     while (Lo < Hi) {
       size_t Mid = Lo + (Hi - Lo) / 2;
-      if (Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[Mid])) {
+      if (isAfter(Ptr, Mid)) {
         Result = static_cast<PTUID>(Mid);
-        Lo = Mid + 1; // still after Mid's checkpoint -- look for a later one
+        Lo = Mid + 1;
       } else {
-        Hi = Mid; // not even after Mid -- must be before it
+        Hi = Mid;
       }
     }
     return Result;
   }
 
-  bool predatesPTU(const ASTContext &Ctx, const void *Ptr, PTUID ID) const {
-    assert(ID < CheckpointBeforePTU.size());
-    return !Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
-  }
-
-  bool isFromThisPTU(const ASTContext &Ctx, const void *Ptr, PTUID ID) const {
-    assert(ID < CheckpointBeforePTU.size());
-    return Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
-  }
-
-  /// Drop checkpoints from \p ID onward.
+  /// Drop checkpoints from \p ID onward, after rolling those PTUs back.
   void undoFrom(PTUID ID) {
     if (ID < CheckpointBeforePTU.size())
       CheckpointBeforePTU.resize(ID);
@@ -651,11 +581,11 @@ template <typename OwnerT, typename ValueT> class FieldMutationChain {
     Entries.push_back(FieldMutation<ValueT>{ID, OldValue});
   }
 
-  const ValueT *mostRecent(const OwnerT *Owner) const {
+  std::optional<ValueT> mostRecent(const OwnerT *Owner) const {
     auto It = Log.find(Owner);
-    return (It == Log.end() || It->second.empty())
-               ? nullptr
-               : &It->second.back().OldValue;
+    if (It == Log.end() || It->second.empty())
+      return std::nullopt;
+    return It->second.back().OldValue;
   }
 
   std::optional<PTUID> mostRecentID(const OwnerT *Owner) const {
@@ -676,24 +606,24 @@ template <typename OwnerT, typename ValueT> class FieldMutationChain {
                            : std::optional<PTUID>(History.front().ID);
   }
 
-  template <typename FnT> void forEachOwnerSince(PTUID ID, FnT &&Fn) const {
-    for (auto &Entry : Log)
-      if (!Entry.second.empty() && Entry.second.back().ID >= ID)
-        Fn(Entry.first);
-  }
+  // template <typename FnT> void forEachOwnerSince(PTUID ID, FnT &&Fn) const {
+  //   for (auto &Entry : Log)
+  //     if (!Entry.second.empty() && Entry.second.back().ID >= ID)
+  //       Fn(Entry.first);
+  // }
 
-  void removeFrom(PTUID ID) {
-    llvm::SmallVector<OwnerT *> ToErase;
-    for (auto &Entry : Log) {
-      auto &Entries = Entry.second;
-      while (!Entries.empty() && Entries.back().ID >= ID)
-        Entries.pop_back();
-      if (Entries.empty())
-        ToErase.push_back(Entry.first);
-    }
-    for (const OwnerT *O : ToErase)
-      Log.erase(O);
-  }
+  // void removeFrom(PTUID ID) {
+  //   llvm::SmallVector<OwnerT *> ToErase;
+  //   for (auto &Entry : Log) {
+  //     auto &Entries = Entry.second;
+  //     while (!Entries.empty() && Entries.back().ID >= ID)
+  //       Entries.pop_back();
+  //     if (Entries.empty())
+  //       ToErase.push_back(Entry.first);
+  //   }
+  //   for (const OwnerT *O : ToErase)
+  //     Log.erase(O);
+  // }
 
   /// Same trim-then-erase-if-empty as removeFrom(ID), scoped to one owner
   /// instead of sweeping the whole Log -- for callers that already know
@@ -760,11 +690,9 @@ struct MutationRecord {
 using DeclShape = MutationRecord::DeclShape;
 using MutationType = MutationRecord::MutationKind;
 
-class PTUMutationActions;
-
 struct PTUStateInfo {
   PTUID ID;
-  const TranslationUnitDecl *ThisTU; // current info
+  const TranslationUnitDecl *ThisPTU; // current info
 
   llvm::MapVector<const Decl *, MutationRecord> Mutations;
 
@@ -775,17 +703,9 @@ struct PTUStateInfo {
 
   /// Exactly the decls this PTU's own commit() pushed a footprint-chain
   /// entry for (DefinitionData/Specialization/MemberSpecialization, via
-  /// chainFor()/memberSpecChainFor()), keyed on the canonical decl the
-  /// same way those accessors are. Lets undo walk only what this PTU
-  /// actually touched instead of sweeping every decl ever tracked across
-  /// every PTU -- the exact opposite of commit(), scoped the same way
-  /// commit() itself is scoped.
+  /// chainFor()/memberSpecChainFor()).
   llvm::SmallPtrSet<const Decl *, 8> TouchedFootprintDecls;
 
-  /// Same idea for the two FieldMutationChain logs (FunctionTypeMutations,
-  /// TagdeclInfos), which are keyed by owner rather than living in
-  /// LinkedDecls -- kept as two separate sets since the owner types
-  /// (FunctionDecl vs TagDecl) differ.
   llvm::SmallPtrSet<const FunctionDecl *, 4> TouchedFunctionTypeOwners;
   llvm::SmallPtrSet<const TagDecl *, 4> TouchedTagdeclOwners;
 
@@ -852,10 +772,11 @@ class SweepTracker {
       Active.erase(It);
   }
 
-  // Call at commit(ID) time. PTUMutationActions is invoked as
+  // Call at commit/restore time. OnConfirmed is invoked as
   // (const Decl *D, DeclShape S, uint32_t ConfirmedKinds) for every decl
   // that had something newly confirmed this sweep.
-  void sweep(PTUMutationActions &Act);
+  template <typename OnConfirmedFn>
+  void sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed);
 };
 
 //===----------------------------------------------------------------------===//
@@ -878,105 +799,58 @@ struct CXXClassDeclNode {
   PTUID OriginID;
   RecordDeclDefinitionDataChain *DefData = nullptr;
   llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *> Spec;
-  CXXClassDeclNode *Next = nullptr; // free-list link, meaningless off the list
 };
 
 struct VarDeclNode {
   PTUID OriginID;
   llvm::PointerUnion<VarSpecializationChain *, MemberSpecializationChain *>
       Spec;
-  VarDeclNode *Next = nullptr;
 };
 
 struct FunctionDeclNode {
   PTUID OriginID;
   llvm::PointerUnion<FunctionSpecializationChain *, MemberSpecializationChain *>
       Spec;
-  FunctionDeclNode *Next = nullptr;
 };
 
 struct EnumDeclNode {
   PTUID OriginID;
   MemberSpecializationChain *MemberSpec =
       nullptr; // the only thing an enum can ever have
-  EnumDeclNode *Next = nullptr;
 };
 
 using DeclLinkedState = llvm::PointerUnion<CXXClassDeclNode *, VarDeclNode *,
                                            FunctionDeclNode *, EnumDeclNode *>;
 
-/// Intrusive free-list pool for the four node types above. Released nodes
-/// are kept for reuse instead of being immediately deleted. The pool is
-/// bounded by Capacity; once it is full, additional released nodes are
-/// deleted instead of being kept. This keeps memory usage bounded while
-/// still allowing freed nodes to be reused.
-
-template <typename T> class NodePool {
-  T *FreeList = nullptr;
-  unsigned FreeCount = 0;
-  unsigned Capacity;
-
-public:
-  explicit NodePool(unsigned Capacity = 64) : Capacity(Capacity) {}
-
-  ~NodePool() {
-    while (FreeList) {
-      T *Dead = FreeList;
-      FreeList = FreeList->Next;
-      delete Dead;
-    }
-  }
-  /// Hands back a reset (all-default) node -- recycled if one is free,
-  /// freshly allocated otherwise.
-  T *acquire() {
-    if (T *N = FreeList) {
-      FreeList = N->Next;
-      --FreeCount;
-      *N = T();
-      return N;
-    }
-    return new T();
-  }
-  /// Takes ownership back. Kept for a future acquire() to hand out again
-  /// if the free list is under capacity; reclaimed for real (delete)
-  /// otherwise, so this pool never holds more than Capacity dead nodes.
-  void release(T *N) {
-    if (FreeCount >= Capacity) {
-      delete N;
-      return;
-    }
-    N->Next = FreeList;
-    FreeList = N;
-    ++FreeCount;
-  }
-};
-
-/// Pool for reusing chain objects without modifying StateAwareChain.
-/// Released chains are kept for reuse, and the pool is bounded by Capacity
-/// so unused chains do not cause unbounded memory growth.
-template <typename ChainT> class ChainPool {
-  llvm::SmallVector<ChainT *, 8> Free;
+/// Pool for reusing node/chain objects without requiring any intrusive
+/// free-list field on T itself -- freed pointers are tracked in a side
+/// vector instead, so this works for the four node types above and the
+/// five StateAwareChain-based chain types alike. Released objects are kept
+/// for reuse, and the pool is bounded by Capacity so unused objects do not
+/// cause unbounded memory growth.
+template <typename T> class ObjectPool {
+  llvm::SmallVector<T *, 8> Free;
   unsigned Capacity;
 
 public:
-  explicit ChainPool(unsigned Capacity = 64) : Capacity(Capacity) {}
+  explicit ObjectPool(unsigned Capacity = 64) : Capacity(Capacity) {}
 
-  ~ChainPool() {
-    for (ChainT *C : Free)
+  ~ObjectPool() {
+    for (T *C : Free)
       delete C;
   }
 
-  ChainT *acquire() {
+  T *acquire() {
     if (!Free.empty()) {
-      ChainT *C = Free.pop_back_val();
-      *C = ChainT();
+      T *C = Free.pop_back_val();
+      *C = T();
       return C;
     }
-    return new ChainT();
+    return new T();
   }
-  /// Same capacity rule as NodePool::release() -- reclaimed for real once
-  /// Free is at capacity, rather than growing without bound.
-  void release(ChainT *C) {
+  /// Reclaimed for real once Free is at capacity, rather than growing
+  /// without bound.
+  void release(T *C) {
     if (Free.size() >= Capacity) {
       delete C;
       return;
@@ -986,16 +860,16 @@ template <typename ChainT> class ChainPool {
 };
 
 class LinkedDeclNodeGenerator {
-  NodePool<CXXClassDeclNode> RecordNodes;
-  NodePool<VarDeclNode> VarNodes;
-  NodePool<FunctionDeclNode> FunctionNodes;
-  NodePool<EnumDeclNode> EnumNodes;
+  ObjectPool<CXXClassDeclNode> RecordNodes;
+  ObjectPool<VarDeclNode> VarNodes;
+  ObjectPool<FunctionDeclNode> FunctionNodes;
+  ObjectPool<EnumDeclNode> EnumNodes;
 
-  ChainPool<RecordDeclDefinitionDataChain> DefDataChains;
-  ChainPool<SpecializationChain> ClassSpecChains;
-  ChainPool<VarSpecializationChain> VarSpecChains;
-  ChainPool<FunctionSpecializationChain> FunctionSpecChains;
-  ChainPool<MemberSpecializationChain> MemberSpecChains;
+  ObjectPool<RecordDeclDefinitionDataChain> DefDataChains;
+  ObjectPool<SpecializationChain> ClassSpecChains;
+  ObjectPool<VarSpecializationChain> VarSpecChains;
+  ObjectPool<FunctionSpecializationChain> FunctionSpecChains;
+  ObjectPool<MemberSpecializationChain> MemberSpecChains;
 
 public:
   CXXClassDeclNode *acquireRecordNode() { return RecordNodes.acquire(); }
@@ -1055,9 +929,12 @@ enum class LangMode : uint8_t {
 class IncrementalStateTracker {
 private:
   ASTContext &Ctx;
+  // Only needed so remove stale
+  // Sema::SpecialMemberCache entries for decls this PTU created.
+  Sema &SemaRef;
+  PTUCheckpointLedger PTUSlabCheckpoints;
   LangMode Mode;
   PTUID NextID = 0;
-  PTUCheckpointLedger PTUSlabCheckpoints;
 
   mutable std::vector<PTUStateInfo> PTUStack;
 
@@ -1072,7 +949,7 @@ class IncrementalStateTracker {
   SweepTracker HiddenMutationTracker;
 
   FunctionExceptionSpecChain FunctionTypeMutations;
-  TypeForDeclChain TagdeclInfos;
+  TypeForDeclChain TagDeclTypes;
 
   friend class PTUMutationActions;
 
@@ -1083,10 +960,15 @@ class IncrementalStateTracker {
   }
   void noteTagDeclTypeMutation(PTUID ID, const TagDecl *Owner,
                                const Type *OldValue) {
-    TagdeclInfos.noteMutation(ID, Owner, OldValue);
+    TagDeclTypes.noteMutation(ID, Owner, OldValue);
     current().TouchedTagdeclOwners.insert(Owner);
   }
 
+  TypeForDeclChain &getTagDeclTypeInfo() { return TagDeclTypes; }
+  FunctionExceptionSpecChain &getFunctionTypeMutations() {
+    return FunctionTypeMutations;
+  }
+
   /// Get-or-create the CXXClassDeclNode backing Canon, acquiring one from
   /// the generator on first use. The only place a CXXClassDeclNode is
   /// created for this map.
@@ -1238,7 +1120,8 @@ class IncrementalStateTracker {
   }
 
 public:
-  IncrementalStateTracker(ASTContext &Ctx, LangMode M) : Ctx(Ctx), Mode(M) {}
+  IncrementalStateTracker(ASTContext &Ctx, Sema &S, LangMode M)
+      : Ctx(Ctx), SemaRef(S), PTUSlabCheckpoints(Ctx), Mode(M) {}
 
   LangMode getMode() const { return Mode; }
 
@@ -1257,7 +1140,7 @@ class IncrementalStateTracker {
   }
 
   bool isFromThisPTU(const void *Ptr, PTUID ID) const {
-    return PTUSlabCheckpoints.isFromThisPTU(Ctx, Ptr, ID);
+    return PTUSlabCheckpoints.isFromThisPTU(Ptr, ID);
   }
   bool isFromCurrentPTU(const void *Ptr) const {
     return isFromThisPTU(Ptr, currentID());
@@ -1265,6 +1148,7 @@ class IncrementalStateTracker {
 
   SweepTracker &getHiddenMutationTracker() { return HiddenMutationTracker; }
   PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
+  Sema &getSema() const { return SemaRef; }
 
   void undoLastEntries();
 
@@ -1284,6 +1168,14 @@ class IncrementalStateTracker {
       llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *>
           &Spec,
       PTUID ID, LinkedDeclNodeGenerator &Gen);
+  static bool removeVarSpec(
+      llvm::PointerUnion<VarSpecializationChain *, MemberSpecializationChain *>
+          &Spec,
+      PTUID ID, LinkedDeclNodeGenerator &Gen);
+  static bool
+  removeFunctionSpec(llvm::PointerUnion<FunctionSpecializationChain *,
+                                        MemberSpecializationChain *> &Spec,
+                     PTUID ID, LinkedDeclNodeGenerator &Gen);
   void removeLinkedDecl(const Decl *D, PTUID ID);
   // just only remove entries from current();
 };
@@ -1291,11 +1183,23 @@ class IncrementalStateTracker {
 class PTUMutationActions {
 private:
   IncrementalStateTracker &Tracker;
+  SweepTracker &HiddenMutationTracker;
+  DeclStateReverter Reverter;
 
 public:
   explicit PTUMutationActions(IncrementalStateTracker &Tracker)
-      : Tracker(Tracker) {}
+      : Tracker(Tracker),
+        HiddenMutationTracker(Tracker.getHiddenMutationTracker()),
+        Reverter(Tracker.getPTUSlabCheckpoints()) {}
+
+  template <typename DeclStateProxyT>
+  void walkDecls(const DeclContext *DC, DeclStateProxyT &Proxy);
+
+  uint32_t kindsNeedingTracking(DeclShape S, const Decl *D);
 
+  uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
+
+private:
   /// Every mutation this PTU recorded for a class-shaped decl, applied in
   /// dependency order: definition data first (later steps read the completed
   /// definition), then the type cache, then specialization footprints, then
@@ -1341,45 +1245,29 @@ class PTUMutationActions {
   void commitEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec,
                         bool IsNew);
 
-  template <typename DeclStateProxyT>
-  void walkDecls(const DeclContext *DC, DeclStateProxyT &Proxy);
+  void restoreClass(PTUID ID, const CXXRecordDecl *RD, MutationRecord &Rec);
 
-  // static uint32_t classifyPossibleKinds(DeclShape S, const Decl *D);
+  void restoreFunction(PTUID ID, const FunctionDecl *FD, MutationRecord &Rec);
 
-  uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
+  void restoreTemplate(PTUID ID, const RedeclarableTemplateDecl *TD,
+                       MutationRecord &Rec);
+
+  void restoreVar(PTUID ID, const VarDecl *VD, MutationRecord &Rec);
+
+  void restoreEnum(PTUID ID, const EnumDecl *ED, MutationRecord &Rec);
 
-  void commitLevel1(PTUID ID, const Decl *D, MutationRecord &Rec,
-                    bool IsNew = false);
+  void restoreTypedef(PTUID ID, const TypedefNameDecl *TD, MutationRecord &Rec);
 
+public:
+  void restoreDecl(PTUID ID, const Decl *D, MutationRecord &Rec);
+  void commitDecl(PTUID ID, const Decl *D, MutationRecord &Rec,
+                  bool IsNew = false);
   // respect the LIFO so only current inside map not commited can be commited
   // not randon PTUID
   // global map info shouldn't be commited before only added here. not note*
   // time.
   void commit(TranslationUnitDecl *MostRecentTU);
-
-  void restoreClassFamily(PTUID ID, const CXXRecordDecl *RD,
-                          MutationRecord &Rec);
-
-  void restoreFunctionFamily(PTUID ID, const FunctionDecl *FD,
-                             MutationRecord &Rec);
-
-  void restoreTemplateFamily(PTUID ID, const RedeclarableTemplateDecl *TD,
-                             MutationRecord &Rec);
-
-  void restoreVarFamily(PTUID ID, const VarDecl *VD, MutationRecord &Rec);
-
-  /// Every mutation this PTU recorded for an enum-shaped decl, or -- when CR
-  /// is non-null -- the baseline seed for one this PTU created.
-  ///
-  /// The smallest family: enums have no specialization category (there is no
-  /// such thing as an enum template), no deferred bodies, and no members with
-  /// independent mutable state -- EnumConstantDecls live and die with the
-  /// EnumDecl, so they are not separately tracked.
-  void restoreEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec);
-
-  void restoreLevel1(PTUID ID, const Decl *D, MutationRecord &Rec);
-
-  void rollback(TranslationUnitDecl *MostRecentTU);
+  void restore(TranslationUnitDecl *MostRecentTU);
 };
 
 class PTUMutationRecorder : public ASTMutationListener {
@@ -1388,51 +1276,48 @@ class PTUMutationRecorder : public ASTMutationListener {
 
   template <typename TemplateT, typename SpecT>
   void noteTemplateDeclMutation(const TemplateT *TD, const SpecT *Spec,
-                                DeclShape S) {}
+                                DeclShape S);
 
   void noteExceptionSpecMutation(const FunctionDecl *FD,
-                                 MutationType K = MutationType::ExceptionSpec) {
-  }
+                                 MutationType K = MutationType::ExceptionSpec);
 
-  void noteDefinitionInstantiated(const Decl *D) {}
+  void noteDefinitionInstantiated(const Decl *D);
 
 public:
   PTUMutationRecorder(IncrementalStateTracker &Tracker) : Tracker(Tracker) {}
 
   /// A new TagDecl definition was completed.
-  void CompletedTagDefinition(const TagDecl *D) override {}
+  void CompletedTagDefinition(const TagDecl *D) override;
 
   /// A new declaration with name has been added to a DeclContext.
-  void AddedVisibleDecl(const DeclContext *DC, const Decl *D) override {}
+  void AddedVisibleDecl(const DeclContext *DC, const Decl *D) override;
 
   /// An implicit member was added after the definition was completed.
-  void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) override {
-  }
+  void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) override;
 
   /// A template specialization (or partial one) was added to the
   /// template declaration.
   void AddedCXXTemplateSpecialization(
       const ClassTemplateDecl *TD,
-      const ClassTemplateSpecializationDecl *D) override {}
+      const ClassTemplateSpecializationDecl *D) override;
 
   /// A template specialization (or partial one) was added to the
   /// template declaration.
   void AddedCXXTemplateSpecialization(
       const VarTemplateDecl *TD,
-      const VarTemplateSpecializationDecl *D) override {}
+      const VarTemplateSpecializationDecl *D) override;
 
   /// A template specialization (or partial one) was added to the
   /// template declaration.
   void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD,
-                                      const FunctionDecl *D) override {}
+                                      const FunctionDecl *D) override;
 
   /// A function's exception specification has been evaluated or
   /// instantiated.
-  void ResolvedExceptionSpec(const FunctionDecl *FD) override {}
+  void ResolvedExceptionSpec(const FunctionDecl *FD) override;
 
   /// A function's return type has been deduced.
-  void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override {
-  }
+  void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override;
 
   /// A virtual destructor's operator delete has been resolved.
   void ResolvedOperatorDelete(const CXXDestructorDecl *DD,
@@ -1460,7 +1345,7 @@ class PTUMutationRecorder : public ASTMutationListener {
   /// The instantiation of a templated function or variable was
   /// requested. In particular, the point of instantiation and template
   /// specialization kind of \p D may have changed.
-  void InstantiationRequested(const ValueDecl *D) override {}
+  void InstantiationRequested(const ValueDecl *D) override;
 
   /// A templated variable's definition was implicitly instantiated.
   void VariableDefinitionInstantiated(const VarDecl *D) override {
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index 01d3295d1ac30..e2e0c48feb8a6 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -22,6 +22,7 @@ endif()
 add_clang_library(clangInterpreter
   DeviceOffload.cpp
   CodeCompletion.cpp
+  ErrRecovery.cpp
   IncrementalAction.cpp
   IncrementalExecutor.cpp
   OrcIncrementalExecutor.cpp
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
new file mode 100644
index 0000000000000..b5070b7f47b7b
--- /dev/null
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -0,0 +1,1321 @@
+//===--------- ErrorRecovery.cpp - Declaration State Recovery -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements declaration state recovery for failed
+// partial translation units (PTUs), restoring declaration state to
+// the state of the previous PTU.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclContextInternals.h"
+#include "clang/Interpreter/ErrorRecovery.h"
+#include "clang/Sema/Sema.h"
+
+namespace clang {
+
+/// -----------------------------------------------------------------------
+//////////////////////// PTUMutationActions::Helpers ///////////////////////
+/// -----------------------------------------------------------------------
+
+DefinitionDataFootprint *
+DeclStateReverter::createDefinitionDataFootprint(const ASTContext &Ctx,
+                                                 const CXXRecordDecl &RD) {
+  auto *FP =
+      new (Ctx, alignof(DefinitionDataFootprint)) DefinitionDataFootprint();
+  const auto &Live = RD.data();
+#define FIELD(Name, Width, Merge) FP->Name = Live.Name;
+#include "clang/AST/CXXRecordDeclDefinitionBits.def"
+  return FP;
+}
+
+bool DeclStateReverter::compareDefinitionDataFootprint(
+    const DefinitionDataFootprint &FP, const CXXRecordDecl &RD) {
+  const auto &Live = RD.data();
+#define FIELD(Name, Width, Merge)                                              \
+  if (FP.Name != Live.Name)                                                    \
+    return false;
+#include "clang/AST/CXXRecordDeclDefinitionBits.def"
+  return true;
+}
+
+void DeclStateReverter::restoreDefinitionDataFootprint(
+    const DefinitionDataFootprint &FP, CXXRecordDecl &RD) {
+  auto &Live = RD.data();
+#define FIELD(Name, Width, Merge) Live.Name = FP.Name;
+#include "clang/AST/CXXRecordDeclDefinitionBits.def"
+}
+
+// NEEDS: friend class DeclStateReverter; in CXXRecordDecl (DefinitionData
+// is PRIVATE there -- already granted) and nothing further for the
+// DeclContext/TagDecl parts below, which are routed through the
+// protected accessors above instead.
+void DeclStateReverter::restoreDefinitionAndRevertDC(CXXRecordDecl &RD) {
+  // 1. DefinitionData pointer -- back to null.
+  RD.DefinitionData = nullptr;
+
+  // 2. DeclContext member list -- back to empty. RD's own FirstDecl/
+  // LastDecl were populated by InstantiateClassImpl substituting RD's
+  // members for the first time (SemaTemplateInstantiate.cpp:3658-3729,
+  // and for the static_assert/friend-decl case specifically,
+  // Sema::BuildStaticAssertDeclaration's CurContext->addDecl(), which
+  // never notifies since neither is a NamedDecl) -- an incomplete,
+  // never-defined class's DeclContext has neither, so this is RD's
+  // true prior state, not a guess.
+  clearDeclContextChain(RD);
+
+  // 3. TagDecl completion bits -- back to "never started". Flipped by
+  // startDefinition()/completeDefinition().
+  clearBeingDefined(RD);
+  RD.setCompleteDefinition(false);
+}
+
+void DeclStateReverter::revertDefinitionArrival(Decl &D) {
+  if (auto *FD = dyn_cast<FunctionDecl>(&D))
+    FD->setBody(nullptr);
+  else if (auto *VD = dyn_cast<VarDecl>(&D))
+    VD->setInit(nullptr);
+  else if (auto *Field = dyn_cast<FieldDecl>(&D))
+    Field->setInClassInitializer(nullptr);
+}
+
+void DeclStateReverter::removeSpecializations(
+    PTUCheckpointLedger &Ledger, const RedeclarableTemplateDecl *TD, PTUID ID) {
+  if (const auto *CTD = dyn_cast<ClassTemplateDecl>(TD)) {
+    auto &Specs =
+        static_cast<const ClassTemplateSpecAccess &>(*CTD).getSpecializations();
+    while (!Specs.empty()) {
+      auto It = Specs.end();
+      --It;
+      if (!Ledger.isFromThisPTU(&*It, ID))
+        break;
+      Specs.pop_back();
+    }
+  } else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(TD)) {
+    auto &Specs = static_cast<const FunctionTemplateSpecAccess &>(*FTD)
+                      .getSpecializations();
+    while (!Specs.empty()) {
+      auto It = Specs.end();
+      --It;
+      if (!Ledger.isFromThisPTU(It->getFunction(), ID))
+        break;
+      Specs.pop_back();
+    }
+  } else if (const auto *VTD = dyn_cast<VarTemplateDecl>(TD)) {
+    auto &Specs =
+        static_cast<const VarTemplateSpecAccess &>(*VTD).getSpecializations();
+    while (!Specs.empty()) {
+      auto It = Specs.end();
+      --It;
+      if (!Ledger.isFromThisPTU(&*It, ID))
+        break;
+      Specs.pop_back();
+    }
+  }
+}
+
+void DeclStateReverter::detachDefData(const Decl *D) {
+  // TODO: handle Template's own separate redecl chain the same way.
+  const auto *RD = dyn_cast<CXXRecordDecl>(D);
+  if (!RD)
+    return;
+  for (const auto *I : RD->redecls())
+    const_cast<CXXRecordDecl *>(cast<CXXRecordDecl>(I))->DefinitionData =
+        nullptr;
+}
+
+void DeclStateReverter::detachCommonBase(const RedeclarableTemplateDecl *RT) {
+  // Common is shared across the WHOLE redecl chain once path-compression
+  // (RedeclarableTemplateDecl::getCommonPtr()'s backward walk-then-
+  // backfill) has run -- resetting only the one decl this PTU created
+  // would leave some other redecl holding a stale, dangling copy of the
+  // same pointer. Walk every redecl, not just a backward chain from RT.
+  for (const auto *R : RT->redecls())
+    clearCommonPtr(*R);
+}
+
+/// Remove D from every lookup map it became visible in, reinstating the
+/// previous declaration where D had replaced one in-place (which is what
+/// StoredDeclsList::HandleRedeclaration does on a redeclaration --
+/// erasing the slot outright would lose the older decl entirely; that is
+/// the ReopenNs failure).
+void DeclStateReverter::detachFromDCLookup(Decl *D, PTUID ID) {
+  NamedDecl *ND = dyn_cast<NamedDecl>(D);
+  if (!ND)
+    return; // nothing nameable -- never entered a lookup map
+
+  // Chain must still be intact to find the replacement -- that is why
+  // this runs before detachFromRedeclChain.
+  NamedDecl *Survivor = findSurvivor(ND, ID);
+  DeclarationName Name = ND->getDeclName();
+
+  // A decl in a transparent context (an unnamed namespace, an unscoped
+  // enum, a linkage-spec block) is visible in the enclosing context's map
+  // as well, so every level it was inserted into needs the same repair --
+  // not just its own primary context.
+  DeclContext *DC = ND->getDeclContext();
+  do {
+    DeclContext *Primary = DC->getPrimaryContext();
+    if (StoredDeclsMap *Map = Primary->getLookupPtr()) {
+      auto Pos = Map->find(Name);
+      // Not an error: the map is built lazily, so a context that was
+      // never looked up in has no entry for this name at all.
+      if (Pos != Map->end()) {
+        StoredDeclsList &List = Pos->second;
+        List.remove(ND);
+        if (Survivor)
+          List.addOrReplaceDecl(Survivor);
+        if (List.isNull())
+          Map->erase(Pos);
+      }
+    }
+  } while (DC->isTransparentContext() && (DC = DC->getParent()));
+
+  //   DeclContext *Lexical = const_cast<DeclContext
+  //   *>(ND->getLexicalDeclContext()); if
+  //   (RepairedLexicalContexts.insert(Lexical).second)
+  //     repairLexicalChain(*Lexical);
+}
+
+// Walk D's redecl chain looking for the newest decl that predates this
+// PTU. Returns nullptr if the entire chain was created this PTU.
+template <typename DeclT>
+DeclT *DeclStateReverter::findSurvivor(DeclT *D, PTUID ID) const {
+  for (Decl *It = D->getMostRecentDecl(); It; It = It->getPreviousDecl()) {
+    if (!PTUSlabCheckpoints.isFromThisPTU(D, ID))
+      return dyn_cast<DeclT>(It);
+  }
+  return nullptr;
+}
+
+/// Point the canonical decl's "most recent" link back at the newest
+/// redeclaration that predates this PTU.
+void DeclStateReverter::detachFromRedeclChain(const Decl *D, PTUID ID) {
+  (void)tryDetachRedeclChain(const_cast<Decl *>(D), ID);
+}
+
+void DeclStateReverter::repairLexicalChain(DeclContext &DC, PTUID ID) {
+  auto &Access = static_cast<DeclContextLinkAccess &>(DC);
+  Decl *Prev = nullptr;
+  for (Decl *Cur = Access.FirstDecl; Cur; Cur = Cur->getNextDeclInContext()) {
+    if (PTUSlabCheckpoints.isFromThisPTU(Cur, ID)) {
+      if (Prev) {
+        Access.LastDecl = Prev;
+        clearNextInContext(*Prev);
+      } else {
+        Access.FirstDecl = Access.LastDecl = nullptr; // nothing survives
+      }
+      return;
+    }
+    Prev = Cur;
+  }
+}
+
+NamedDecl *DeclStateReverter::tryDetachRedeclChain(Decl *D, PTUID ID) {
+  if (hasNoPreviousDecl(D))
+    return nullptr; // nothing to do
+
+  NamedDecl *Survivor = nullptr;
+
+  auto unlinkRedeclChain = [&](auto *RD) {
+    auto *SurvivorDecl = findSurvivor(RD, ID);
+    if (SurvivorDecl)
+      patchRedeclLink(RD, SurvivorDecl);
+
+    Survivor = SurvivorDecl;
+  };
+
+  switch (D->getKind()) {
+  case Decl::Function:
+    unlinkRedeclChain(cast<FunctionDecl>(D));
+    break;
+
+  case Decl::Var:
+    unlinkRedeclChain(cast<VarDecl>(D));
+    break;
+
+  case Decl::Enum:
+  case Decl::Record:
+  case Decl::CXXRecord:
+    unlinkRedeclChain(cast<TagDecl>(D));
+    break;
+
+  case Decl::ClassTemplate: {
+    // NamedDecl *ND = cast<RedeclarableTemplateDecl>(D)->getTemplatedDecl();
+    unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
+    // tryDetachRedeclChain(ND);
+  } break;
+
+  case Decl::FunctionTemplate:
+    unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
+    break;
+
+  case Decl::TypeAliasTemplate:
+    unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
+    break;
+
+  case Decl::VarTemplate:
+    unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
+    break;
+
+  case Decl::Namespace:
+    unlinkRedeclChain(cast<NamespaceDecl>(D));
+    break;
+
+  default:
+    break;
+  }
+
+  return Survivor;
+}
+
+static DeclShape classifyShape(const Decl *D) {
+  // Base shape -- most-derived first.
+  if (isa<RedeclarableTemplateDecl>(D))
+    return DeclShape::Template;
+  else if (isa<EnumDecl>(D))
+    return DeclShape::Enum;
+  else if (isa<FunctionDecl>(D))
+    return DeclShape::Function;
+  else if (isa<VarDecl>(D))
+    return DeclShape::Var;
+  else if (isa<CXXRecordDecl>(D))
+    return DeclShape::Class;
+  else if (isa<TypedefDecl, TypeAliasDecl>(D))
+    return DeclShape::Typedef;
+  else
+    return DeclShape::None;
+}
+
+/// Called once when a decl is first seen. This only sets up the possible
+/// MutationRecord kinds; it does not detect the mutation itself.
+///
+/// Add a kind here only if we need to do something before the mutation:
+/// - save the old value because it will be overwritten before the listener
+///   runs, or
+/// - start hidden tracking because there is no listener for that change.
+///
+/// If the listener itself is enough to detect the change, don't add it here.
+/// noteMutated() will create the record when the change actually happens.
+///
+/// Explicit specializations are terminal, so their SpecInfo/MemberSpecInfo
+/// does not need to be tracked.
+uint32_t PTUMutationActions::kindsNeedingTracking(DeclShape S, const Decl *D) {
+  switch (S) {
+  case DeclShape::Class: {
+    const auto *RD = cast<CXXRecordDecl>(D);
+    uint32_t Kinds = 0;
+    // TypeForDecl is written twice for TagDecls. Keep tracking until both
+    // writes are done.
+    // if (const Type *T = DeclStateReverter::getRawTypeForDecl(RD);
+    //     !T || T->isCanonicalUnqualified())
+    //   Kinds |= uint32_t(MutationType::TypeForDecl);
+    if (RD->hasDefinition() && RD == RD->getDefinition()) {
+      // Do we really need to create a DefData chain here? Usually, an implicit
+      // decl can be mutated later after the definition. If it is already wired
+      // correctly, there is nothing to mutate or track here.
+      //
+      // Save the current DefinitionData because it may change later.
+      if (RD->needsImplicitDefaultConstructor() ||
+          RD->needsImplicitCopyConstructor() ||
+          RD->needsImplicitCopyAssignment() ||
+          RD->needsImplicitMoveConstructor() ||
+          RD->needsImplicitMoveAssignment() || RD->needsImplicitDestructor())
+        Kinds |= uint32_t(MutationType::DefinitionData);
+    }
+    if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+      if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization)
+        Kinds |= uint32_t(MutationType::SpecInfo);
+    } else if (const auto *MSI = RD->getMemberSpecializationInfo()) {
+      // Same terminal state, same enum, reused for MSI's own Kind/POI.
+      if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+        Kinds |= uint32_t(MutationType::MemberSpecInfo);
+    }
+    return Kinds;
+  }
+  case DeclShape::Function: {
+    const auto *FD = cast<FunctionDecl>(D);
+    uint32_t Kinds = 0;
+    const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
+    // Exception-spec resolution changes the type before the listener runs,
+    // so we need the old type saved beforehand.
+    if (FPT && (FPT->getExceptionSpecType() == EST_Unevaluated ||
+                FPT->getExceptionSpecType() == EST_Uninstantiated ||
+                FPT->getExceptionSpecType() == EST_DependentNoexcept ||
+                FPT->getExceptionSpecType() == EST_Unparsed))
+      Kinds |= uint32_t(MutationType::ExceptionSpec);
+    if (FD->getReturnType()->isUndeducedType())
+      // Deduced return type works the same way: the type is changed before
+      // the listener runs, so save it beforehand.
+      Kinds |= uint32_t(MutationType::DeducedReturnType);
+    if (FD->getTemplateSpecializationInfo() &&
+        FD->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+      // A specialization’s Kind/point-of-instantiation is overwritten in place,
+      // with no separate record of the old value. The footprint chain is the
+      // only place where the previous value is preserved.
+      Kinds |= uint32_t(MutationType::SpecInfo);
+    if (const auto *MSI = FD->getMemberSpecializationInfo()) {
+      if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+        Kinds |= uint32_t(MutationType::MemberSpecInfo);
+    }
+
+    return Kinds;
+  }
+  case DeclShape::Var: {
+    const auto *VD = cast<VarDecl>(D);
+    uint32_t Kinds = 0;
+    if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
+      // Same terminal-state exclusion as the Class/Function cases above.
+      if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization)
+        Kinds |= uint32_t(MutationType::SpecInfo);
+    } else if (const auto *MSI = VD->getMemberSpecializationInfo()) {
+      if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+        Kinds |= uint32_t(MutationType::MemberSpecInfo);
+    }
+    // EvaluatedStmt::WasEvaluated is set the first time this variable is
+    // constant-evaluated, which can happen in any PTU long after the VarDecl
+    // was created. There is no ASTMutationListener event for this
+    // (VarDecl::evaluateValueImpl doesn’t notify anything), so this is a
+    // hidden kind that we have to check live here, just like
+    // Shape::Template’s CommonCreated.
+    //
+    // getEvaluatedStmt() is a pure read – unlike evaluateValue(), it doesn’t
+    // trigger evaluation itself, so it’s safe to check on every classify call.
+    if (const EvaluatedStmt *Eval = VD->getEvaluatedStmt();
+        !Eval || !Eval->WasEvaluated)
+      Kinds |= uint32_t(MutationType::EvaluatedValue);
+
+    return Kinds;
+  }
+  case DeclShape::Enum: {
+    const auto *ED = cast<EnumDecl>(D);
+    uint32_t Kinds = 0;
+    // TypeForDecl: EnumDecl is also a TagDecl, so the same two-write pattern
+    // and “still open” condition as the Class case above apply here – see
+    // its comment for the details.
+    //
+    // if (const Type *T = DeclStateReverter::getRawTypeForDecl(ED);
+    //     !T || T->isCanonicalUnqualified())
+    //   Kinds |= uint32_t(MutationType::TypeForDecl);
+    //
+    // MSI-backed part: for an ordinary enum, its own completion is the
+    // redeclaration-creates-a-new-object case. EnumDecl is Redeclarable just
+    // like CXXRecordDecl, so the same reasoning as Function/Var applies here.
+    if (const auto *MSI = ED->getMemberSpecializationInfo()) {
+      // Same terminal-state exclusion as the other MSI-backed cases above.
+      if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+        Kinds |= uint32_t(MutationType::MemberSpecInfo);
+    }
+    return Kinds;
+  }
+  case DeclShape::Template: {
+    uint32_t Kinds = 0;
+    // TODO: track only canon decl; and also add check
+    // HiddenMutationTracker.isTrackedFor(RT->getCanonicalDecl()) is not being
+    // already tracked
+    const RedeclarableTemplateDecl *RT =
+        cast<RedeclarableTemplateDecl>(D->getCanonicalDecl());
+    if (!DeclStateReverter::isCommonPtrValid(*RT) &&
+        !HiddenMutationTracker.isTrackedFor(
+            RT, uint32_t(MutationType::TemplateCommon))) {
+      // No listener reports these Common changes, so use hidden tracking.
+      Kinds |= uint32_t(MutationType::TemplateCommon);
+    } else if (const auto *CTD = dyn_cast<ClassTemplateDecl>(RT)) {
+      // Common exists, so calling getCommonPtr() here is safe (won't
+      // allocate) -- only ClassTemplateDecl has CanonInjectedTST in its
+      // own Common, not the Function/Var template siblings.
+      if (!DeclStateReverter::isTemplateCanonInjectedTSTValid(CTD))
+        Kinds |= uint32_t(MutationType::CanonInjectedTST);
+    }
+    return Kinds;
+  }
+  case DeclShape::Typedef: {
+    // TypedefDecl/TypeAliasDecl only -- ASTContext::getTypedefType() has its
+    // own guard (if (Decl->TypeForDecl) // return), so this is effectively
+    // write-once: once non-null, TypeForDecl
+    // never changes again for this decl.
+    // const auto *TD = cast<TypedefNameDecl>(D);
+    // if (!DeclStateReverter::getRawTypeForDecl(TD))
+    //   return uint32_t(MutationType::TypeForDecl);
+    return 0;
+  }
+  case DeclShape::None:
+    return 0;
+  }
+  return 0;
+}
+
+/// Given a Decl known to be DeclShape S with FlaggedKinds set, return the
+/// subset of kinds that are actually confirmed.
+///
+/// Everything reaching this function through verifyMutations() belongs to
+/// a decl from an earlier PTU. Same-PTU mutations are ignored by the
+/// reactive listeners, so these mutations were already found during an
+/// earlier commit().
+///
+/// Some mutation kinds are fully confirmed by their listener, so there is
+/// no need to walk the chain for them:
+/// - DefinitionInstantiate, SpecializationAdded, DefinitionData
+/// - ExceptionSpec, DeducedReturnType
+///
+/// For these, the listener itself is the source of truth for the change.
+/// In particular, don’t use the FunctionTypeMutations chain for
+/// ExceptionSpec/DeducedReturnType here. That chain is populated later by
+/// commitFunctionFamily(), so checking it here would miss the first
+/// resolution.
+///
+/// The remaining kinds don’t have a reliable listener, so they need to be
+/// checked against the stored state:
+/// - SpecInfo, MemberSpecInfo
+/// - Template Common/CanonInjectedTST
+/// - Var EvaluatedValue
+/// - Typedef TypeForDecl
+///
+/// For footprint-based checks, the baseline should already be there since
+/// it is seeded when the decl is first discovered. If it’s missing here,
+/// assert in debug builds, but still confirm the mutation. commitDecl()
+/// will create the missing chain while committing, which fixes up the
+/// baseline for future checks.
+uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
+                                               uint32_t FlaggedKinds) {
+
+  uint32_t Verified = 0;
+  Verified |= FlaggedKinds & uint32_t(MutationType::DefinitionInstantiate);
+  switch (S) {
+  case DeclShape::Class: {
+    const auto *RD = cast<CXXRecordDecl>(D);
+    // AddedCXXImplicitMember firing is itself the confirmation -- see
+    // this function's own top comment for why no chain lookup belongs
+    // here.
+    Verified |= FlaggedKinds & uint32_t(MutationType::DefinitionData);
+    if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
+      //   const Type *Now = DeclStateReverter::getRawTypeForDecl(RD);
+      //   if (Tracker.getTagDeclTypeInfo().mostRecent(RD).value_or(nullptr) !=
+      //   Now)
+      //     Verified |= uint32_t(MutationType::TypeForDecl);
+    }
+    if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
+      if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+        auto *Chain = Tracker.getChainFor(Spec);
+        assert(Chain && "SpecInfo baseline missing at verify time");
+        const SpecializationFootprint *Last =
+            Chain ? Chain->mostRecent() : nullptr;
+        if (!Last ||
+            !DeclStateReverter::compareSpecializationFootprint(*Last, *Spec))
+          Verified |= uint32_t(MutationType::SpecInfo);
+      }
+    } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
+      auto *Chain = Tracker.getMemberSpecChainFor(RD);
+      assert(Chain && "MemberSpecInfo baseline missing at verify time");
+      const MemberSpecializationFootprint *Last =
+          Chain ? Chain->mostRecent() : nullptr;
+      if (!Last ||
+          !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *RD))
+        Verified |= uint32_t(MutationType::MemberSpecInfo);
+    }
+    break;
+  }
+  case DeclShape::Function: {
+    const auto *FD = cast<FunctionDecl>(D);
+    if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
+      auto *Chain = Tracker.getChainFor(FD);
+      assert(Chain && "SpecInfo baseline missing at verify time");
+      const FunctionSpecializationFootprint *Last =
+          Chain ? Chain->mostRecent() : nullptr;
+      if (!Last || !DeclStateReverter::compareFunctionSpecializationFootprint(
+                       *Last, *FD))
+        Verified |= uint32_t(MutationType::SpecInfo);
+    } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
+      auto *Chain = Tracker.getMemberSpecChainFor(FD);
+      assert(Chain && "MemberSpecInfo baseline missing at verify time");
+      const MemberSpecializationFootprint *Last =
+          Chain ? Chain->mostRecent() : nullptr;
+      if (!Last ||
+          !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *FD))
+        Verified |= uint32_t(MutationType::MemberSpecInfo);
+    }
+    // ResolvedExceptionSpec/DeducedReturnType firing is itself the
+    // confirmation.
+    Verified |= FlaggedKinds & (uint32_t(MutationType::ExceptionSpec) |
+                                uint32_t(MutationType::DeducedReturnType));
+    break;
+  }
+  case DeclShape::Var: {
+    const auto *VD = cast<VarDecl>(D);
+    if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
+      if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
+        auto *Chain = Tracker.getChainFor(Spec);
+        assert(Chain && "SpecInfo baseline missing at verify time");
+        const VarSpecializationFootprint *Last =
+            Chain ? Chain->mostRecent() : nullptr;
+        if (!Last ||
+            !DeclStateReverter::compareVarSpecializationFootprint(*Last, *Spec))
+          Verified |= uint32_t(MutationType::SpecInfo);
+      }
+    } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
+      auto *Chain = Tracker.getMemberSpecChainFor(VD);
+      assert(Chain && "MemberSpecInfo baseline missing at verify time");
+      const MemberSpecializationFootprint *Last =
+          Chain ? Chain->mostRecent() : nullptr;
+      if (!Last ||
+          !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *VD))
+        Verified |= uint32_t(MutationType::MemberSpecInfo);
+    }
+    if (FlaggedKinds & uint32_t(MutationType::EvaluatedValue) &&
+        Tracker.getHiddenMutationTracker().isTrackedFor(
+            VD, uint32_t(MutationType::EvaluatedValue))) {
+      // No chain to compare against -- write-once field, so presence of
+      // WasEvaluated=true right now IS the confirmation, same reasoning
+      // as Shape::Template's CommonCreated/CanonInjectedTSTCached above.
+      const EvaluatedStmt *Eval = VD->getEvaluatedStmt();
+      if (Eval && Eval->WasEvaluated)
+        Verified |= uint32_t(MutationType::EvaluatedValue);
+    }
+    break;
+  }
+  case DeclShape::Enum: {
+    const auto *ED = cast<EnumDecl>(D);
+    // if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
+    //   const Type *Now = DeclStateReverter::getRawTypeForDecl(ED);
+    //   if (Tracker.getTagDeclTypeInfo().mostRecent(ED).value_or(nullptr) !=
+    //   Now)
+    //     Verified |= uint32_t(MutationType::TypeForDecl);
+    // }
+    if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
+      auto *Chain = Tracker.getMemberSpecChainFor(ED);
+      assert(Chain && "MemberSpecInfo baseline missing at verify time");
+      const MemberSpecializationFootprint *Last =
+          Chain ? Chain->mostRecent() : nullptr;
+      if (!Last ||
+          !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *ED))
+        Verified |= uint32_t(MutationType::MemberSpecInfo);
+    }
+    break;
+  }
+  case DeclShape::Template: {
+    // SpecializationAdded is known directly from
+    // AddedCXXTemplateSpecialization firing, so
+    // always confirmed when flagged.
+    Verified |= FlaggedKinds & uint32_t(MutationType::SpecializationAdded);
+    const auto *RT = cast<RedeclarableTemplateDecl>(D)->getCanonicalDecl();
+    if (FlaggedKinds & uint32_t(MutationType::TemplateCommon) &&
+        Tracker.getHiddenMutationTracker().isTrackedFor(
+            RT, uint32_t(MutationType::TemplateCommon))) {
+      // No chain to compare against -- write-once, so Common's own
+      // existence right now IS the confirmation.
+      if (DeclStateReverter::isCommonPtrValid(*RT))
+        Verified |= uint32_t(MutationType::TemplateCommon);
+    }
+    if (FlaggedKinds & uint32_t(MutationType::CanonInjectedTST) &&
+        Tracker.getHiddenMutationTracker().isTrackedFor(
+            RT, uint32_t(MutationType::CanonInjectedTST))) {
+      if (const auto *CTD = dyn_cast<ClassTemplateDecl>(RT))
+        if (DeclStateReverter::isTemplateCanonInjectedTSTValid(CTD))
+          Verified |= uint32_t(MutationType::CanonInjectedTST);
+    }
+  } break;
+  case DeclShape::Typedef: {
+    const auto *TD = cast<TypedefNameDecl>(D);
+    if (FlaggedKinds & uint32_t(MutationType::TypeForDecl) &&
+        // Defensive, on top of SweepTracker::sweep()'s own bookkeeping --
+        // same reasoning as the Var/Template cases above.
+        Tracker.getHiddenMutationTracker().isTrackedFor(
+            TD, uint32_t(MutationType::TypeForDecl))) {
+      // No chain to compare against -- write-once field (unlike
+      // TagDecl's TypeForDecl, this one is guarded by ASTContext's own
+      // `if (Decl->TypeForDecl) return`, never overwritten a second
+      // time), so non-null right now IS the confirmation.
+      //   if (DeclStateReverter::getRawTypeForDecl(TD))
+      //     Verified |= uint32_t(MutationType::TypeForDecl);
+    }
+  } break;
+  case DeclShape::None:
+    break; // never flagged in the first place.
+  }
+  return Verified;
+}
+
+template <typename OnConfirmedFn>
+void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
+  llvm::SmallVector<std::pair<const Decl *, uint32_t>, 8> Confirmed;
+  for (auto &Entry : Active) {
+    const Decl *D = Entry.getFirst();
+    uint32_t StillOpen = Entry.getSecond();
+    DeclShape S = classifyShape(D);
+    if (uint32_t Newly = Act.verifyMutationFor(D, S, StillOpen)) {
+      OnConfirmed(D, S, Newly);
+      Confirmed.emplace_back(D, Newly);
+    }
+  }
+  for (auto &[D, Newly] : Confirmed)
+    settle(D, Newly);
+}
+
+/// Verify recorded mutations and remove any spurious ones. If none of a
+/// decl’s mutation kinds are confirmed, remove the record entirely so
+/// restore() does not pop chain entries that were never written.
+void PTUStateInfo::verifyMutations(PTUMutationActions &Actions) {
+  Mutations.remove_if([&](auto &Entry) -> bool {
+    uint32_t Confirmed = Actions.verifyMutationFor(Entry.first, Entry.second.S,
+                                                   Entry.second.MutationType);
+    Entry.second.MutationType = Confirmed;
+    return Confirmed == 0;
+  });
+}
+
+class DeclStateCommitPolicy {
+  PTUID ID;
+  PTUMutationActions &Action;
+
+public:
+  DeclStateCommitPolicy(PTUID ID, PTUMutationActions &Action)
+      : ID(ID), Action(Action) {}
+  /// Everything shouldAct resolved, carried to the caller instead of
+  /// stashed. Empty means "skip this decl" -- there's no separate bool.
+  struct CommitAction {
+    DeclShape S;
+    uint32_t Kinds;
+    explicit operator bool() const { return Kinds != 0; }
+  };
+
+  bool shouldRecurse(const Decl *D) const {
+    if (isa<NamespaceDecl>(D))
+      return true;
+    if (const auto *RD = dyn_cast<CXXRecordDecl>(D))
+      return RD->isCompleteDefinition();
+    return false;
+  }
+
+  CommitAction actionFor(const Decl *D) const {
+    if (!D->isDefinedOutsideFunctionOrMethod())
+      return {};
+
+    DeclShape S = classifyShape(D);
+    if (S == DeclShape::None)
+      return {};
+
+    // we have to handle every case that if a decl need to create baseline
+    // info or if any decl has done all mutations already in this for exmpla
+    // membe spec already properly done like this kind of case so we don't
+    // create spec info blindly
+    uint32_t Kinds = Action.kindsNeedingTracking(S, D);
+    if (!Kinds)
+      return {}; // structurally immutable -- no baseline needed
+
+    return CommitAction{S, Kinds};
+  }
+
+  void runAction(const Decl *D, const CommitAction &A) {
+    MutationRecord Rec = MutationRecord{A.S, A.Kinds};
+    Action.commitDecl(ID, D, Rec, /*IsNew=*/true);
+  }
+};
+
+class DeclStateUnlinkPolicy {
+  PTUID ID;
+  IncrementalStateTracker &Tracker;
+  DeclStateReverter &Reverter;
+  llvm::SmallPtrSet<const Decl *, 16> RepairedChains;
+  llvm::SmallPtrSet<const DeclContext *, 8> RepairedLexicalContexts;
+  llvm::SmallPtrSet<const DeclContext *, 8> TouchedDC;
+
+public:
+  DeclStateUnlinkPolicy(PTUID ID, IncrementalStateTracker &Tracker,
+                        DeclStateReverter &Reverter)
+      : ID(ID), Tracker(Tracker), Reverter(Reverter) {}
+
+  struct RestoreAction {
+    bool DetachFromLexicalChain = false;
+    bool DetachFromDC = false;
+    bool DetachFromRedecl = false;
+    bool DetachDefData = false;
+    // bool DetachCommonBase = false;
+    bool HasTemplated = false;
+    bool TemplatedDetachDefData = false;
+    explicit operator bool() const {
+      return DetachFromLexicalChain || DetachFromDC || DetachFromRedecl ||
+             DetachDefData || /*DetachCommonBase ||*/ HasTemplated ||
+             TemplatedDetachDefData;
+    }
+  };
+
+  bool shouldRecurse(const Decl *D) const {
+    // we don't care about the decl which complete chain is part belong to
+    // current PTU.
+    if (Tracker.isFromThisPTU(D->getCanonicalDecl(), ID))
+      return false;
+    return isa<NamespaceDecl>(D);
+  }
+
+  RestoreAction actionFor(const Decl *D) const {
+    RestoreAction A;
+    const Decl *Canon = D->getCanonicalDecl();
+
+    /// Keyed on the canonical decl: every redeclaration shares one
+    /// RedeclLink, so truncating twice would step past the intended survivor.
+    if (!RepairedChains.count(Canon) && D->getPreviousDecl() &&
+        !Tracker.isFromThisPTU(Canon, ID))
+      A.DetachFromRedecl = true;
+
+    // Keyed on the primary context, a different granularity: a reopened
+    // namespace's redeclarations all resolve to the same primary.
+    const DeclContext *DC = D->getDeclContext()->getPrimaryContext();
+    if (TouchedDC.contains(DC))
+      A.DetachFromDC = true;
+
+    if (D->isImplicit() &&
+        !RepairedLexicalContexts.count(D->getLexicalDeclContext()))
+      A.DetachFromLexicalChain = true;
+
+    if (const auto *RT = dyn_cast<RedeclarableTemplateDecl>(D)) {
+      // if (Tracker.isFromThisPTU(::needToDetachCommonPtr(CheckPoint, RT),
+      // ID))
+      //   A.DetachCommonBase = true;
+
+      const NamedDecl *T = RT->getTemplatedDecl();
+      A.HasTemplated = true;
+      if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(T)) {
+        if (const CXXRecordDecl *Def = RD->getDefinition();
+            Def && Tracker.isFromThisPTU(Def, ID))
+          A.TemplatedDetachDefData = true;
+      }
+    } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
+      if (const CXXRecordDecl *Def = RD->getDefinition();
+          Def && Tracker.isFromThisPTU(Def, ID))
+        A.DetachDefData = true;
+    }
+
+    return A;
+  }
+
+  void runAction(const Decl *D, const RestoreAction &A) {
+    if (A.DetachDefData)
+      Reverter.detachDefData(D);
+    // if (A.DetachCommonBase)
+    //   Detacher.detachCommonBase(D);
+    if (A.DetachFromDC)
+      Reverter.detachFromDCLookup(const_cast<Decl *>(D), ID);
+    if (A.DetachFromRedecl) {
+      RepairedChains.insert(D->getCanonicalDecl());
+      Reverter.detachFromRedeclChain(D, ID);
+    }
+
+    if (A.HasTemplated) {
+      const Decl *T = cast<RedeclarableTemplateDecl>(D)->getTemplatedDecl();
+      if (A.TemplatedDetachDefData)
+        Reverter.detachDefData(T);
+      if (A.DetachFromRedecl)
+        Reverter.detachFromRedeclChain(T, ID);
+    }
+
+    if (A.DetachFromLexicalChain) {
+      Reverter.repairLexicalChain(
+          *const_cast<DeclContext *>(D->getLexicalDeclContext()), ID);
+      RepairedLexicalContexts.insert(D->getLexicalDeclContext());
+    }
+  }
+};
+
+template <typename DeclStateProxyT>
+void PTUMutationActions::walkDecls(const DeclContext *DC,
+                                   DeclStateProxyT &Proxy) {
+  for (const Decl *D : DC->decls()) {
+    if (Proxy.shouldRecurse(D))
+      walkDecls(cast<DeclContext>(D), Proxy);
+    if (auto Act = Proxy.actionFor(D))
+      Proxy.runAction(D, Act);
+  }
+}
+
+/// -----------------------------------------------------------------------
+//////////////////////// PTUMutationActions::restore //////////////////////
+/// -----------------------------------------------------------------------
+
+void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
+                                      MutationRecord &Rec) {
+
+  // DefinitionArrived and DefinitionDataChanged commit identically -- both
+  // mean "this record's DefinitionData needs a fresh footprint." They stay
+  // distinct kinds because rollback and dependency-edge attribution treat
+  // them differently (arrival is this PTU completing a record that
+  // predates it; a data change may be either).
+  if (Rec.has(MutationType::DefinitionInstantiate) ||
+      Rec.has(MutationType::DefinitionData)) {
+    // CXXRecordDecl *RD = cast<CXXRecordDecl>(RD);
+    // New: no prior entry exists, so write unconditionally.
+    // Existing: only write a real diff.
+    /// SpecializationDecl can have lazy implicit generated body;
+    if (Rec.has(MutationType::DefinitionInstantiate)) {
+      DeclStateReverter::revertDefinitionArrival(
+          *const_cast<CXXRecordDecl *>(RD));
+    } else {
+      auto *Chain = Tracker.getChainFor(RD);
+      if (Chain) {
+        if (const DefinitionDataFootprint *Restored = Chain->mostRecent())
+          DeclStateReverter::restoreDefinitionDataFootprint(
+              *Restored, *const_cast<CXXRecordDecl *>(RD));
+      }
+    }
+  }
+
+  // TypeForDecl is shared across the whole redeclaration chain -- revert all.
+  if (Rec.has(MutationType::TypeForDecl)) {
+    // CXXRecordDecl *RD = cast<CXXRecordDecl>(D);
+    for (const TagDecl *Redecl : RD->redecls()) {
+      const Type *LastKnown =
+          Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+      const TypeDecl *TD = cast<TypeDecl>(Redecl);
+      if (LastKnown != TD->getTypeForDecl())
+        const_cast<TypeDecl *>(TD)->setTypeForDecl(LastKnown);
+    }
+  }
+
+  if (Rec.has(MutationType::SpecInfo)) {
+    // Only a specialization can carry this kind; a plain CXXRecordDecl
+    // reaching here means a note*() wrapper passed the wrong kind.
+    const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD);
+    assert(Spec && "SpecializationAdded noted on a non-specialization");
+    if (auto *Chain = Tracker.getChainFor(Spec)) {
+      if (const SpecializationFootprint *Restored = Chain->mostRecent())
+        DeclStateReverter::restoreSpecializationFootprint(
+            *Restored, *const_cast<ClassTemplateSpecializationDecl *>(Spec));
+    }
+  }
+
+  if (Rec.has(MutationType::MemberSpecInfo)) {
+    // A nested member of a class template – its MSI kind or
+    // point-of-instantiation changed.
+    assert(RD->getMemberSpecializationInfo());
+    if (auto *Chain = Tracker.getMemberSpecChainFor(RD)) {
+      if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
+        DeclStateReverter::restoreMemberSpecializationFootprint(
+            *Restored, *const_cast<CXXRecordDecl *>(RD));
+    }
+  }
+}
+
+void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
+                                         MutationRecord &Rec) {
+
+  // FunctionType is shared across the whole redeclaration chain.
+  // Resolving a deferred noexcept or deducing an auto return updates the
+  // type for every redecl, including ones owned by earlier PTUs, so each
+  // redecl needs to be recorded rather than just FD’s own.
+  if (Rec.has(MutationType::ExceptionSpec) ||
+      Rec.has(MutationType::DeducedReturnType)) {
+    for (const FunctionDecl *Redecl : FD->redecls()) {
+      QualType LastKnown =
+          Tracker.getFunctionTypeMutations().mostRecent(Redecl).value_or(
+              QualType());
+      if (LastKnown != Redecl->getType())
+        const_cast<FunctionDecl *>(Redecl)->setType(LastKnown);
+    }
+  }
+
+  if (Rec.has(MutationType::DefinitionInstantiate))
+    DeclStateReverter::revertDefinitionArrival(*const_cast<FunctionDecl *>(FD));
+
+  if (Rec.has(MutationType::SpecInfo)) {
+    // A function template specialization is a plain FunctionDecl carrying
+    // FunctionTemplateSpecializationInfo.
+    assert(FD->getTemplateSpecializationKind() != TSK_Undeclared &&
+           "SpecInfoChanged on a function with no specialization info");
+    if (auto *Chain = Tracker.getChainFor(FD)) {
+      if (const FunctionSpecializationFootprint *Restored = Chain->mostRecent())
+        DeclStateReverter::restoreFunctionSpecializationFootprint(
+            *Restored, *const_cast<FunctionDecl *>(FD));
+    }
+  }
+
+  if (Rec.has(MutationType::MemberSpecInfo)) {
+    assert(FD->getMemberSpecializationInfo());
+    if (auto *Chain = Tracker.getMemberSpecChainFor(FD)) {
+      if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
+        DeclStateReverter::restoreMemberSpecializationFootprint(
+            *Restored, *const_cast<FunctionDecl *>(FD));
+    }
+  }
+}
+
+void PTUMutationActions::restoreTemplate(PTUID ID,
+                                         const RedeclarableTemplateDecl *TD,
+                                         MutationRecord &Rec) {
+
+  if (Rec.has(MutationType::TemplateCommon)) {
+    // TODO: call DeclStateReverter to detech all decl's common ptr if canon
+    // decl of template ie being tracked;
+    // DeclStateReverter::resetTemplateCommonBase(
+    //     *const_cast<RedeclarableTemplateDecl *>(TD));
+    DeclStateReverter::detachCommonBase(TD);
+    // Common’s creation was done by this PTU.
+    // Re-register both bits, since a later PTU may trigger either one again,
+    // and CanonInjectedTST cannot exist until Common exists again first.
+    //
+    // TODO: Track only the canonical decl.
+    HiddenMutationTracker.track(TD,
+                                uint32_t(MutationType::TemplateCommon) |
+                                    uint32_t(MutationType::CanonInjectedTST));
+  } else if (Rec.has(MutationType::CanonInjectedTST)) {
+    if (const auto *CTD = dyn_cast<ClassTemplateDecl>(TD)) {
+      DeclStateReverter::resetCanonInjectedTST(
+          *const_cast<ClassTemplateDecl *>(CTD));
+      HiddenMutationTracker.track(TD, uint32_t(MutationType::CanonInjectedTST));
+    }
+  }
+
+  if (Rec.has(MutationType::SpecializationAdded))
+    DeclStateReverter::removeSpecializations(Tracker.getPTUSlabCheckpoints(),
+                                             TD, ID);
+}
+
+void PTUMutationActions::restoreTypedef(PTUID ID, const TypedefNameDecl *TD,
+                                        MutationRecord &Rec) {
+  if (Rec.has(MutationType::TypeForDecl)) {
+    // TD survives this rollback (only the cached Type was this PTU's
+    // doing) -- reset the live field and re-register: some later PTU
+    // could re-trigger ASTContext::getTypedefType's first-call write.
+    // DeclStateReverter::resetTypeForDecl(const_cast<TypedefNameDecl *>(TD));
+    // HiddenMutationTracker.track(TD, uint32_t(MutationType::TypeForDecl));
+  }
+}
+
+void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
+                                    MutationRecord &Rec) {
+
+  using VarMutation = MutationType;
+
+  // EvaluatedStmt::WasEvaluated is write-once, so its previous state is always
+  // null here. If it made it into Rec, this PTU had confirmed it as evaluated.
+  // Reverting it is therefore just a reset, not a snapshot restore – the
+  // original initializer expression in Eval->Value stays untouched; we only
+  // clear the cached result.
+  if (Rec.has(VarMutation::EvaluatedValue)) {
+    // VD itself survives this rollback (only its cached value was this
+    // PTU's doing) -- reset the live field, and re-register with
+    // SweepTracker since kindsNeedingTracking will flag this as open
+    // again and some later PTU could re-trigger evaluation.
+    if (EvaluatedStmt *Eval = VD->getEvaluatedStmt()) {
+      Eval->WasEvaluated = false;
+      Eval->Evaluated = APValue();
+    }
+    HiddenMutationTracker.track(VD, uint32_t(VarMutation::EvaluatedValue));
+  }
+
+  if (Rec.has(VarMutation::DefinitionInstantiate)) {
+    /// TODO;
+    DeclStateReverter::revertDefinitionArrival(*const_cast<VarDecl *>(VD));
+  }
+
+  if (Rec.has(VarMutation::SpecInfo)) {
+    // Unlike functions, a variable specialization IS a distinct type.
+    const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
+    if (auto *Chain = Tracker.getChainFor(Spec)) {
+      if (const VarSpecializationFootprint *Restored = Chain->mostRecent())
+        DeclStateReverter::restoreVarSpecializationFootprint(
+            *Restored, *const_cast<VarTemplateSpecializationDecl *>(Spec));
+    }
+  }
+
+  if (Rec.has(VarMutation::MemberSpecInfo)) {
+    assert(VD->getMemberSpecializationInfo());
+    if (auto *Chain = Tracker.getMemberSpecChainFor(VD)) {
+      if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
+        DeclStateReverter::restoreMemberSpecializationFootprint(
+            *Restored, *const_cast<VarDecl *>(VD));
+    }
+  }
+}
+
+void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
+                                     MutationRecord &Rec) {
+
+  // Like a class, an EnumDecl's TypeForDecl is shared across its whole
+  // redeclaration chain (`enum class E : int;` forward-declared, then
+  // defined later), so every redecl's cached pointer needs checking.
+  if (Rec.has(MutationType::TypeForDecl)) {
+    for (const TagDecl *Redecl : ED->redecls()) {
+      const Type *LastKnown =
+          Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+      const TypeDecl *TD = cast<TypeDecl>(Redecl);
+      if (LastKnown != TD->getTypeForDecl())
+        const_cast<TypeDecl *>(TD)->setTypeForDecl(LastKnown);
+    }
+  }
+
+  if (Rec.has(MutationType::DefinitionInstantiate))
+    DeclStateReverter::revertDefinitionArrival(*const_cast<EnumDecl *>(ED));
+
+  if (Rec.has(MutationType::MemberSpecInfo)) {
+    // A scoped member enumeration of a class template -- instantiated with
+    // the enclosing specialization, carrying MSI back to the pattern enum.
+    assert(ED->getMemberSpecializationInfo());
+    if (auto *Chain = Tracker.getMemberSpecChainFor(ED)) {
+      if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
+        DeclStateReverter::restoreMemberSpecializationFootprint(
+            *Restored, *const_cast<EnumDecl *>(ED));
+    }
+  }
+}
+
+void PTUMutationActions::restoreDecl(PTUID ID, const Decl *D,
+                                     MutationRecord &Rec) {
+  switch (Rec.S) {
+  case DeclShape::Class:
+    restoreClass(ID, cast<CXXRecordDecl>(D), Rec);
+    break;
+  case DeclShape::Function:
+    restoreFunction(ID, cast<FunctionDecl>(D), Rec);
+    break;
+  case DeclShape::Var:
+    restoreVar(ID, cast<VarDecl>(D), Rec);
+    break;
+  case DeclShape::Enum:
+    restoreEnum(ID, cast<EnumDecl>(D), Rec);
+    break;
+  case DeclShape::Template:
+    restoreTemplate(ID, cast<RedeclarableTemplateDecl>(D), Rec);
+    break;
+  case DeclShape::Typedef:
+    restoreTypedef(ID, cast<TypedefNameDecl>(D), Rec);
+    break;
+  case DeclShape::None:
+    break;
+  }
+}
+
+void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
+  PTUStateInfo &Cur = Tracker.current();
+  assert(ThisTU == Cur.ThisPTU);
+  //   TranslationUnitDecl *ThisTU = Cur.ThisPTU;
+  PTUID ID = Cur.ID;
+  if (Cur.Commited)
+    Tracker.undoLastEntries();
+  else {
+    // TODO: verify Mutations;
+    Cur.verifyMutations(*this);
+    Tracker.getHiddenMutationTracker().sweep(
+        *this, [&](const Decl *D, DeclShape S, uint32_t K) {
+          Cur.noteMutated(D, S, K);
+        });
+  }
+
+  for (auto &[D, Rec] : Cur.Mutations)
+    restoreDecl(ID, D, Rec);
+
+  DeclStateReverter Detacher(Tracker.getPTUSlabCheckpoints());
+  DeclStateUnlinkPolicy Proxy(ID, Tracker, Detacher);
+
+  for (const Decl *D : Cur.ImplicitDecls)
+    if (auto A = Proxy.actionFor(D))
+      Proxy.runAction(D, A);
+
+  walkDecls(ThisTU, Proxy);
+
+  // Sema::SpecialMemberCache (public) caches, per (RD, kind+qualifiers), the
+  // CXXMethodDecl* a prior LookupSpecialMember() call resolved to -- and on
+  // a cache hit it returns that decl directly, with no re-validation at all
+  // (SemaLookup.cpp). Any entry whose cached method was created by the PTU
+  // being rolled back is now stale, so purge it here rather than trying to
+  // reconstruct which of the (RD, kind) keys it could occupy.
+  {
+    Sema &SemaRef = Tracker.getSema();
+    llvm::SmallVector<Sema::SpecialMemberCacheKey, 8> Stale;
+    for (auto &Entry : SemaRef.SpecialMemberCache) {
+      CXXMethodDecl *MD = Entry.second.getMethod();
+      if (MD &&
+          (Tracker.isFromThisPTU(MD, ID) || Cur.ImplicitDecls.contains(MD)))
+        Stale.push_back(Entry.first);
+    }
+    for (const auto &Key : Stale)
+      SemaRef.SpecialMemberCache.erase(Key);
+  }
+
+  // Must be last: Cur/ID/ThisTU are all references into (or derived from)
+  // the PTU this call retires, and popCurrentPTU() destroys that entry.
+  // Runs for BOTH branches above (committed or not) -- a PTU that never
+  // committed still needs its checkpoint-ledger slot and PTUStack
+  // entry reclaimed, otherwise every failed input would permanently grow
+  // both, and NextID would desync from PTUSlabCheckpoints on top of that.
+  Tracker.popCurrentPTU();
+}
+
+/// -----------------------------------------------------------------------
+////////////////////////// MutationListener ///////////////////////////////
+/// -----------------------------------------------------------------------
+
+void PTUMutationRecorder::CompletedTagDefinition(const TagDecl *D) {
+  if (!D->isDefinedOutsideFunctionOrMethod())
+    return; // ordinary local class -- structurally unreachable by any other
+            // PTU
+
+  PTUStateInfo &Cur = Tracker.current();
+  if (Tracker.isFromThisPTU(D, Tracker.currentID())) {
+    if (D->isImplicit())
+      Cur.ImplicitDecls.insert(D);
+    return;
+  }
+  // Predates this PTU: a genuine cross-PTU mutation -- needs the full
+  // definition-data footprint captured in commit().
+  // Cur.TouchedTagDecls.insert(D);
+  DeclShape S = classifyShape(D);
+
+  /// doing this for memspec because we are not sure that if this cause member
+  /// spec mutation or not we can't put compare check here as well since
+  /// notifer never guarranty that will called before mutation or later. so
+  /// this check about mutation will be handled in verification layer
+  Cur.noteMutated(D, S, MutationType::DefinitionInstantiate);
+  if (S == DeclShape::Enum) {
+    if (const EnumDecl *ED = dyn_cast<EnumDecl>(D);
+        ED && ED->getMemberSpecializationInfo())
+      Cur.noteMutated(D, S, MutationType::MemberSpecInfo);
+  } else if (S == DeclShape::Class) {
+    if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D);
+        RD && RD->getMemberSpecializationInfo())
+      Cur.noteMutated(D, S, MutationType::MemberSpecInfo);
+    if (isa<ClassTemplateSpecializationDecl>(D))
+      Cur.noteMutated(D, S, MutationType::SpecInfo);
+  }
+}
+
+void PTUMutationRecorder::AddedVisibleDecl(const DeclContext *DC,
+                                           const Decl *D) {
+  PTUStateInfo &Cur = Tracker.current();
+  if (D->isImplicit())
+    Cur.ImplicitDecls.insert(D);
+
+  if (Tracker.isFromThisPTU(DC, Cur.ID) || Cur.TouchedDC.contains(DC))
+    return;
+  //   auto Origin =
+  //       Tracker.getPTUSlabCheckpoints().attribute(Tracker.Ctx, DC);
+  //   assert(Origin && "must have existing PTUID");
+  //   assert(*Origin != Cur.ID);
+  Cur.TouchedDC.insert(DC);
+}
+
+void PTUMutationRecorder::AddedCXXImplicitMember(const CXXRecordDecl *RD,
+                                                 const Decl *D) {
+  if (!RD->isDefinedOutsideFunctionOrMethod())
+    return; // ordinary local class -- structurally unreachable by any other
+            // PTU
+
+  PTUStateInfo &Cur = Tracker.current();
+
+  if (D->isImplicit())
+    Cur.ImplicitDecls.insert(D);
+  // cover this part as well
+  // if (RD->MemberInfo and D is implcit plus has MSIInfo)
+
+  // cover only not from this ptu.
+  if (Tracker.isFromThisPTU(RD, Cur.ID))
+    return;
+
+  Cur.noteMutated(RD, DeclShape::Class, MutationType::DefinitionData);
+}
+
+template <typename TemplateT, typename SpecT>
+void PTUMutationRecorder::noteTemplateDeclMutation(const TemplateT *TD,
+                                                   const SpecT *Spec,
+                                                   DeclShape S) {
+  PTUStateInfo &Cur = Tracker.current();
+
+  if (Spec->isImplicit())
+    Cur.ImplicitDecls.insert(Spec);
+
+  if (Tracker.isFromThisPTU(TD, Cur.ID))
+    return;
+
+  Cur.noteMutated(TD, DeclShape::Template, MutationType::SpecializationAdded);
+}
+
+void PTUMutationRecorder::AddedCXXTemplateSpecialization(
+    const ClassTemplateDecl *TD, const ClassTemplateSpecializationDecl *D) {
+  noteTemplateDeclMutation(TD, TD, DeclShape::Class);
+}
+
+void PTUMutationRecorder::AddedCXXTemplateSpecialization(
+    const VarTemplateDecl *TD, const VarTemplateSpecializationDecl *D) {
+  noteTemplateDeclMutation(TD, TD, DeclShape::Var);
+}
+
+void PTUMutationRecorder::AddedCXXTemplateSpecialization(
+    const FunctionTemplateDecl *TD, const FunctionDecl *D) {
+  noteTemplateDeclMutation(TD, TD, DeclShape::Function);
+}
+
+void PTUMutationRecorder::noteExceptionSpecMutation(const FunctionDecl *FD,
+                                                    MutationType K) {
+  PTUStateInfo &Cur = Tracker.current();
+  if (Tracker.isFromThisPTU(FD, Cur.ID))
+    return;
+  Cur.noteMutated(FD, DeclShape::Function, K);
+}
+
+void PTUMutationRecorder::ResolvedExceptionSpec(const FunctionDecl *FD) {
+  noteExceptionSpecMutation(FD);
+}
+
+void PTUMutationRecorder::DeducedReturnType(const FunctionDecl *FD,
+                                            QualType ReturnType) {
+  noteExceptionSpecMutation(FD, MutationType::DeducedReturnType);
+}
+
+void PTUMutationRecorder::noteDefinitionInstantiated(const Decl *D) {
+  PTUStateInfo &Cur = Tracker.current();
+  if (Tracker.isFromThisPTU(D, Cur.ID)) {
+    return;
+  }
+  Cur.noteMutated(D, classifyShape(D), MutationType::DefinitionInstantiate);
+}
+
+/// InstantiationRequested fires when Sema decides a template entity needs
+/// instantiating -- before the instantiation actually happens.
+void PTUMutationRecorder::InstantiationRequested(const ValueDecl *D) {
+  PTUStateInfo &Cur = Tracker.current();
+
+  if (Tracker.isFromThisPTU(D, Cur.ID)) {
+    // Created this PTU -- the creation path covers it, no mutation to note.
+    // But an implicitly-created decl may never be appended to any lexical
+    // chain, so walkTU would never see it. Record it here as the
+    // only path that will.
+    if (D->isImplicit())
+      Cur.ImplicitDecls.insert(D);
+    return;
+  }
+
+  DeclShape S;
+  MutationType K = MutationType::None;
+
+  if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+    S = DeclShape::Function;
+    if (FD->getMemberSpecializationInfo())
+      K = MutationType::MemberSpecInfo;
+    else if (FD->getTemplateSpecializationKind() != TSK_Undeclared)
+      // A function template specialization is a plain FunctionDecl
+      // carrying FunctionTemplateSpecializationInfo -- there is no
+      // distinct decl type to dyn_cast to, unlike the variable case.
+      K = MutationType::SpecInfo;
+    else
+      return; // neither -- nothing this notifier can be about
+  } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
+    S = DeclShape::Var;
+    if (VD->getMemberSpecializationInfo())
+      K = MutationType::MemberSpecInfo;
+    else if (isa<VarTemplateSpecializationDecl>(VD))
+      K = MutationType::SpecInfo;
+    else
+      return;
+  } else {
+    return; // no other ValueDecl kind participates in instantiation
+  }
+
+  Cur.noteMutated(D, S, K);
+}
+
+} // end namespace clang
\ No newline at end of file

>From 47213dd55649003c79ce6effc0904b6cd8877892 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Fri, 25 Sep 2026 10:28:42 +0530
Subject: [PATCH 08/13] Refactor: ErrRecovery.cpp impl

---
 .../include/clang/Interpreter/ErrorRecovery.h |  67 ++---
 clang/lib/Interpreter/ErrRecovery.cpp         | 276 ++++++++++--------
 2 files changed, 166 insertions(+), 177 deletions(-)

diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 61380ec6ea0ad..4569494a3b2e2 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -893,9 +893,6 @@ class LinkedDeclNodeGenerator {
     return MemberSpecChains.acquire();
   }
 
-  // One release() name per type, dispatched by overload resolution --
-  // the caller doesn't need to know which internal pool a given pointer
-  // belongs to.
   void release(CXXClassDeclNode *N) { RecordNodes.release(N); }
   void release(VarDeclNode *N) { VarNodes.release(N); }
   void release(FunctionDeclNode *N) { FunctionNodes.release(N); }
@@ -1192,58 +1189,28 @@ class PTUMutationActions {
         HiddenMutationTracker(Tracker.getHiddenMutationTracker()),
         Reverter(Tracker.getPTUSlabCheckpoints()) {}
 
-  template <typename DeclStateProxyT>
-  void walkDecls(const DeclContext *DC, DeclStateProxyT &Proxy);
+  template <typename DeclStatePolicyT>
+  void walkDecls(const DeclContext *DC, DeclStatePolicyT &Proxy);
 
-  uint32_t kindsNeedingTracking(DeclShape S, const Decl *D);
+  uint32_t DeclNeedingTracking(DeclShape S, const Decl *D);
 
   uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
 
 private:
-  /// Every mutation this PTU recorded for a class-shaped decl, applied in
-  /// dependency order: definition data first (later steps read the completed
-  /// definition), then the type cache, then specialization footprints, then
-  /// lazily-completed members last (they can add members that the steps above
-  /// would otherwise have missed).
-  ///
-  /// Kinds are a bitmask, not alternatives -- one class can legitimately have
-  /// several set in a single PTU (e.g. a specialization that also had its
-  /// definition data completed), so these are sequential checks, not a switch.
-  void commitClassFamily(PTUID ID, const CXXRecordDecl *RD, MutationRecord &Rec,
-                         bool IsNew);
-
-  /// Every mutation this PTU recorded for a function-shaped decl, or -- when
-  /// CR is non-null -- the baseline seed for one this PTU created.
-  ///
-  /// Kinds are a bitmask, not alternatives: one function can have its
-  /// exception spec resolved AND its body instantiated in the same PTU, so
-  /// these are sequential checks rather than a switch.
-  ///
-  /// Order matters: type-affecting mutations (exception spec, deduced return)
-  /// come first because the specialization footprint below reads the
-  /// function's type; body instantiation comes last because it can only
-  /// happen once everything about the signature is settled.
-  void commitFunctionFamily(PTUID ID, const FunctionDecl *FD,
-                            MutationRecord &Rec, bool IsNew);
-
-  /// Every mutation this PTU recorded for a var-shaped decl, or -- when CR is
-  /// non-null -- the baseline seed for one this PTU created.
-  ///
-  /// Ordering: initializer instantiation first (it produces the expression
-  /// that constant evaluation later consumes), then the cached evaluated
-  /// value, then specialization/member footprints which read both.
-  void commitVarFamily(PTUID ID, const VarDecl *VD, MutationRecord &Rec,
-                       bool IsNew);
-
-  /// Every mutation this PTU recorded for an enum-shaped decl, or -- when CR
-  /// is non-null -- the baseline seed for one this PTU created.
-  ///
-  /// The smallest family: enums have no specialization category (there is no
-  /// such thing as an enum template), no deferred bodies, and no members with
-  /// independent mutable state -- EnumConstantDecls live and die with the
-  /// EnumDecl, so they are not separately tracked.
-  void commitEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec,
-                        bool IsNew);
+  void commitMembers(PTUID ID, const DeclContext *Members);
+
+  void commitClass(PTUID ID, const CXXRecordDecl *RD, MutationRecord &Rec,
+                   bool IsNew);
+  void commitFunction(PTUID ID, const FunctionDecl *FD, MutationRecord &Rec,
+                      bool IsNew);
+  void commitVar(PTUID ID, const VarDecl *VD, MutationRecord &Rec, bool IsNew);
+
+  void commitEnum(PTUID ID, const EnumDecl *ED, MutationRecord &Rec,
+                  bool IsNew);
+  void commitTemplate(PTUID ID, const RedeclarableTemplateDecl *ED,
+                      MutationRecord &Rec, bool IsNew);
+  void commitTypedef(PTUID ID, const TypedefNameDecl *ED, MutationRecord &Rec,
+                     bool IsNew);
 
   void restoreClass(PTUID ID, const CXXRecordDecl *RD, MutationRecord &Rec);
 
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index b5070b7f47b7b..216d896f498bd 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -21,6 +21,14 @@
 
 namespace clang {
 
+#define PTU_SHAPES                                                             \
+  PTU_SHAPE(Class, CXXRecordDecl)                                              \
+  PTU_SHAPE(Function, FunctionDecl)                                            \
+  PTU_SHAPE(Var, VarDecl)                                                      \
+  PTU_SHAPE(Enum, EnumDecl)                                                    \
+  PTU_SHAPE(Template, RedeclarableTemplateDecl)                                \
+  PTU_SHAPE(Typedef, TypedefNameDecl)
+
 /// -----------------------------------------------------------------------
 //////////////////////// PTUMutationActions::Helpers ///////////////////////
 /// -----------------------------------------------------------------------
@@ -177,11 +185,6 @@ void DeclStateReverter::detachFromDCLookup(Decl *D, PTUID ID) {
       }
     }
   } while (DC->isTransparentContext() && (DC = DC->getParent()));
-
-  //   DeclContext *Lexical = const_cast<DeclContext
-  //   *>(ND->getLexicalDeclContext()); if
-  //   (RepairedLexicalContexts.insert(Lexical).second)
-  //     repairLexicalChain(*Lexical);
 }
 
 // Walk D's redecl chain looking for the newest decl that predates this
@@ -254,17 +257,10 @@ NamedDecl *DeclStateReverter::tryDetachRedeclChain(Decl *D, PTUID ID) {
   } break;
 
   case Decl::FunctionTemplate:
-    unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
-    break;
-
   case Decl::TypeAliasTemplate:
-    unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
-    break;
-
   case Decl::VarTemplate:
     unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
     break;
-
   case Decl::Namespace:
     unlinkRedeclChain(cast<NamespaceDecl>(D));
     break;
@@ -307,7 +303,7 @@ static DeclShape classifyShape(const Decl *D) {
 ///
 /// Explicit specializations are terminal, so their SpecInfo/MemberSpecInfo
 /// does not need to be tracked.
-uint32_t PTUMutationActions::kindsNeedingTracking(DeclShape S, const Decl *D) {
+uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
   switch (S) {
   case DeclShape::Class: {
     const auto *RD = cast<CXXRecordDecl>(D);
@@ -334,7 +330,6 @@ uint32_t PTUMutationActions::kindsNeedingTracking(DeclShape S, const Decl *D) {
       if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization)
         Kinds |= uint32_t(MutationType::SpecInfo);
     } else if (const auto *MSI = RD->getMemberSpecializationInfo()) {
-      // Same terminal state, same enum, reused for MSI's own Kind/POI.
       if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
         Kinds |= uint32_t(MutationType::MemberSpecInfo);
     }
@@ -572,8 +567,7 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
         Tracker.getHiddenMutationTracker().isTrackedFor(
             VD, uint32_t(MutationType::EvaluatedValue))) {
       // No chain to compare against -- write-once field, so presence of
-      // WasEvaluated=true right now IS the confirmation, same reasoning
-      // as Shape::Template's CommonCreated/CanonInjectedTSTCached above.
+      // WasEvaluated=true right now IS the confirmation.
       const EvaluatedStmt *Eval = VD->getEvaluatedStmt();
       if (Eval && Eval->WasEvaluated)
         Verified |= uint32_t(MutationType::EvaluatedValue);
@@ -624,8 +618,6 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
   case DeclShape::Typedef: {
     const auto *TD = cast<TypedefNameDecl>(D);
     if (FlaggedKinds & uint32_t(MutationType::TypeForDecl) &&
-        // Defensive, on top of SweepTracker::sweep()'s own bookkeeping --
-        // same reasoning as the Var/Template cases above.
         Tracker.getHiddenMutationTracker().isTrackedFor(
             TD, uint32_t(MutationType::TypeForDecl))) {
       // No chain to compare against -- write-once field (unlike
@@ -644,18 +636,18 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
 
 template <typename OnConfirmedFn>
 void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
-  llvm::SmallVector<std::pair<const Decl *, uint32_t>, 8> Confirmed;
+  //   llvm::SmallVector<std::pair<const Decl *, uint32_t>, 8> Confirmed;
   for (auto &Entry : Active) {
     const Decl *D = Entry.getFirst();
     uint32_t StillOpen = Entry.getSecond();
     DeclShape S = classifyShape(D);
     if (uint32_t Newly = Act.verifyMutationFor(D, S, StillOpen)) {
       OnConfirmed(D, S, Newly);
-      Confirmed.emplace_back(D, Newly);
+      //   Confirmed.emplace_back(D, Newly);
     }
   }
-  for (auto &[D, Newly] : Confirmed)
-    settle(D, Newly);
+  //   for (auto &[D, Newly] : Confirmed)
+  //     settle(D, Newly);
 }
 
 /// Verify recorded mutations and remove any spurious ones. If none of a
@@ -677,13 +669,6 @@ class DeclStateCommitPolicy {
 public:
   DeclStateCommitPolicy(PTUID ID, PTUMutationActions &Action)
       : ID(ID), Action(Action) {}
-  /// Everything shouldAct resolved, carried to the caller instead of
-  /// stashed. Empty means "skip this decl" -- there's no separate bool.
-  struct CommitAction {
-    DeclShape S;
-    uint32_t Kinds;
-    explicit operator bool() const { return Kinds != 0; }
-  };
 
   bool shouldRecurse(const Decl *D) const {
     if (isa<NamespaceDecl>(D))
@@ -693,27 +678,23 @@ class DeclStateCommitPolicy {
     return false;
   }
 
-  CommitAction actionFor(const Decl *D) const {
+  void process(const Decl *D) const {
     if (!D->isDefinedOutsideFunctionOrMethod())
-      return {};
+      return;
 
     DeclShape S = classifyShape(D);
     if (S == DeclShape::None)
-      return {};
+      return;
 
     // we have to handle every case that if a decl need to create baseline
     // info or if any decl has done all mutations already in this for exmpla
     // membe spec already properly done like this kind of case so we don't
     // create spec info blindly
-    uint32_t Kinds = Action.kindsNeedingTracking(S, D);
+    uint32_t Kinds = Action.DeclNeedingTracking(S, D);
     if (!Kinds)
-      return {}; // structurally immutable -- no baseline needed
-
-    return CommitAction{S, Kinds};
-  }
+      return; // structurally immutable -- no baseline needed
 
-  void runAction(const Decl *D, const CommitAction &A) {
-    MutationRecord Rec = MutationRecord{A.S, A.Kinds};
+    MutationRecord Rec{S, Kinds};
     Action.commitDecl(ID, D, Rec, /*IsNew=*/true);
   }
 };
@@ -731,21 +712,6 @@ class DeclStateUnlinkPolicy {
                         DeclStateReverter &Reverter)
       : ID(ID), Tracker(Tracker), Reverter(Reverter) {}
 
-  struct RestoreAction {
-    bool DetachFromLexicalChain = false;
-    bool DetachFromDC = false;
-    bool DetachFromRedecl = false;
-    bool DetachDefData = false;
-    // bool DetachCommonBase = false;
-    bool HasTemplated = false;
-    bool TemplatedDetachDefData = false;
-    explicit operator bool() const {
-      return DetachFromLexicalChain || DetachFromDC || DetachFromRedecl ||
-             DetachDefData || /*DetachCommonBase ||*/ HasTemplated ||
-             TemplatedDetachDefData;
-    }
-  };
-
   bool shouldRecurse(const Decl *D) const {
     // we don't care about the decl which complete chain is part belong to
     // current PTU.
@@ -754,68 +720,59 @@ class DeclStateUnlinkPolicy {
     return isa<NamespaceDecl>(D);
   }
 
-  RestoreAction actionFor(const Decl *D) const {
-    RestoreAction A;
+  void process(const Decl *D) {
     const Decl *Canon = D->getCanonicalDecl();
 
     /// Keyed on the canonical decl: every redeclaration shares one
     /// RedeclLink, so truncating twice would step past the intended survivor.
-    if (!RepairedChains.count(Canon) && D->getPreviousDecl() &&
-        !Tracker.isFromThisPTU(Canon, ID))
-      A.DetachFromRedecl = true;
+    bool DetachFromRedecl = !RepairedChains.count(Canon) &&
+                            D->getPreviousDecl() &&
+                            !Tracker.isFromThisPTU(Canon, ID);
 
     // Keyed on the primary context, a different granularity: a reopened
     // namespace's redeclarations all resolve to the same primary.
     const DeclContext *DC = D->getDeclContext()->getPrimaryContext();
-    if (TouchedDC.contains(DC))
-      A.DetachFromDC = true;
+    bool DetachFromDC = TouchedDC.contains(DC);
 
-    if (D->isImplicit() &&
-        !RepairedLexicalContexts.count(D->getLexicalDeclContext()))
-      A.DetachFromLexicalChain = true;
+    bool DetachFromLexicalChain =
+        D->isImplicit() &&
+        !RepairedLexicalContexts.count(D->getLexicalDeclContext());
 
+    bool DetachDefData = false;
+    bool HasTemplated = false;
+    bool TemplatedDetachDefData = false;
     if (const auto *RT = dyn_cast<RedeclarableTemplateDecl>(D)) {
-      // if (Tracker.isFromThisPTU(::needToDetachCommonPtr(CheckPoint, RT),
-      // ID))
-      //   A.DetachCommonBase = true;
-
       const NamedDecl *T = RT->getTemplatedDecl();
-      A.HasTemplated = true;
+      HasTemplated = true;
       if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(T)) {
         if (const CXXRecordDecl *Def = RD->getDefinition();
             Def && Tracker.isFromThisPTU(Def, ID))
-          A.TemplatedDetachDefData = true;
+          TemplatedDetachDefData = true;
       }
     } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
       if (const CXXRecordDecl *Def = RD->getDefinition();
           Def && Tracker.isFromThisPTU(Def, ID))
-        A.DetachDefData = true;
+        DetachDefData = true;
     }
 
-    return A;
-  }
-
-  void runAction(const Decl *D, const RestoreAction &A) {
-    if (A.DetachDefData)
+    if (DetachDefData)
       Reverter.detachDefData(D);
-    // if (A.DetachCommonBase)
-    //   Detacher.detachCommonBase(D);
-    if (A.DetachFromDC)
+    if (DetachFromDC)
       Reverter.detachFromDCLookup(const_cast<Decl *>(D), ID);
-    if (A.DetachFromRedecl) {
-      RepairedChains.insert(D->getCanonicalDecl());
+    if (DetachFromRedecl) {
+      RepairedChains.insert(Canon);
       Reverter.detachFromRedeclChain(D, ID);
     }
 
-    if (A.HasTemplated) {
+    if (HasTemplated) {
       const Decl *T = cast<RedeclarableTemplateDecl>(D)->getTemplatedDecl();
-      if (A.TemplatedDetachDefData)
+      if (TemplatedDetachDefData)
         Reverter.detachDefData(T);
-      if (A.DetachFromRedecl)
+      if (DetachFromRedecl)
         Reverter.detachFromRedeclChain(T, ID);
     }
 
-    if (A.DetachFromLexicalChain) {
+    if (DetachFromLexicalChain) {
       Reverter.repairLexicalChain(
           *const_cast<DeclContext *>(D->getLexicalDeclContext()), ID);
       RepairedLexicalContexts.insert(D->getLexicalDeclContext());
@@ -823,14 +780,95 @@ class DeclStateUnlinkPolicy {
   }
 };
 
-template <typename DeclStateProxyT>
+template <typename DeclStatePolicyT>
 void PTUMutationActions::walkDecls(const DeclContext *DC,
-                                   DeclStateProxyT &Proxy) {
+                                   DeclStatePolicyT &Policy) {
   for (const Decl *D : DC->decls()) {
-    if (Proxy.shouldRecurse(D))
-      walkDecls(cast<DeclContext>(D), Proxy);
-    if (auto Act = Proxy.actionFor(D))
-      Proxy.runAction(D, Act);
+    if (Policy.shouldRecurse(D))
+      walkDecls(cast<DeclContext>(D), Policy);
+    Policy.process(D);
+  }
+}
+
+/// -----------------------------------------------------------------------
+//////////////////////// PTUMutationActions::commit ///////////////////////
+/// -----------------------------------------------------------------------
+
+void PTUMutationActions::commitMembers(PTUID ID, const DeclContext *Members) {
+  DeclStateCommitPolicy Policy(ID, *this);
+  walkDecls(Members, Policy);
+}
+
+void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
+                                     MutationRecord &Rec, bool IsNew) {
+
+  // TypeForDecl is shared across the whole redeclaration chain – the Type
+  // object itself is owned by whichever redecl first materialized it, so
+  // every redecl’s cached pointer needs to be checked, not just RD’s.
+  if (Rec.has(MutationType::TypeForDecl)) {
+    // for (const TagDecl *Redecl : RD->redecls()) {
+    //   const Type *LastKnown =
+    //       Tracker.TagdeclInfos.mostRecent(Redecl).value_or(nullptr);
+    //    TODO: need to handle?
+    // }
+  }
+
+  // DefinitionArrived and DefinitionDataChanged are committed the same way –
+  // both mean “this record’s DefinitionData needs a fresh footprint.” They
+  // remain separate kinds because rollback and dependency-edge attribution
+  // treat them differently: an arrival means this PTU completed a record
+  // that predates it, while a data change may be either.
+  if (Rec.has(MutationType::DefinitionInstantiate) ||
+      Rec.has(MutationType::DefinitionData)) {
+    Tracker.chainFor(RD).commit(
+        ID, DeclStateReverter::createDefinitionDataFootprint(Tracker.Ctx, *RD));
+
+    /// SpecializationDecl can have lazy implicit generated body;
+    if (Rec.has(MutationType::DefinitionInstantiate)) {
+      if (RD->getMemberSpecializationInfo())
+        commitMembers(ID, cast<DeclContext>(RD));
+    }
+  }
+
+  if (Rec.has(MutationType::SpecInfo)) {
+    // Only a specialization can carry this kind; a plain CXXRecordDecl
+    // reaching here means a note*() wrapper passed the wrong kind.
+    const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD);
+    // assert(Spec && "SpecializationAdded noted on a non-specialization");
+    if (Spec) {
+      Tracker.chainFor(Spec).commit(
+          ID,
+          DeclStateReverter::createSpecializationFootprint(Tracker.Ctx, *Spec));
+      // Member declarations are instantiated eagerly with the class
+      // definition regardless of TSK; only their definitions defer. Walk
+      // them now so each member's MemberSpecializationInfo is tracked from
+      // the moment it exists.
+      if (IsNew && Spec->getSpecializationKind() == TSK_ImplicitInstantiation)
+        commitMembers(ID, cast<DeclContext>(Spec));
+    }
+  }
+
+  if (Rec.has(MutationType::MemberSpecInfo)) {
+    // A nested member of a class template -- its MSI's kind or
+    // point-of-instantiation changed.
+    if (RD->getMemberSpecializationInfo())
+      Tracker.memberSpecChainFor(RD).commit(
+          ID, DeclStateReverter::createMemberSpecializationFootprint(
+                  Tracker.Ctx, *RD));
+  }
+}
+
+void PTUMutationActions::commitDecl(PTUID ID, const Decl *D,
+                                    MutationRecord &Rec, bool IsNew) {
+  switch (Rec.S) {
+#define PTU_SHAPE(NAME, TYPE)                                                  \
+  case DeclShape::NAME:                                                        \
+    commit##NAME(ID, cast<TYPE>(D), Rec, IsNew);                               \
+    break;
+    PTU_SHAPES
+#undef PTU_SHAPE
+  case DeclShape::None:
+    break;
   }
 }
 
@@ -868,13 +906,11 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
   // TypeForDecl is shared across the whole redeclaration chain -- revert all.
   if (Rec.has(MutationType::TypeForDecl)) {
     // CXXRecordDecl *RD = cast<CXXRecordDecl>(D);
-    for (const TagDecl *Redecl : RD->redecls()) {
-      const Type *LastKnown =
-          Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
-      const TypeDecl *TD = cast<TypeDecl>(Redecl);
-      if (LastKnown != TD->getTypeForDecl())
-        const_cast<TypeDecl *>(TD)->setTypeForDecl(LastKnown);
-    }
+    // for (const TagDecl *Redecl : RD->redecls()) {
+    //   const Type *LastKnown =
+    //       Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+    //   TODO: need to handle?
+    // }
   }
 
   if (Rec.has(MutationType::SpecInfo)) {
@@ -999,7 +1035,7 @@ void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
   if (Rec.has(VarMutation::EvaluatedValue)) {
     // VD itself survives this rollback (only its cached value was this
     // PTU's doing) -- reset the live field, and re-register with
-    // SweepTracker since kindsNeedingTracking will flag this as open
+    // SweepTracker since DeclNeedingTracking will flag this as open
     // again and some later PTU could re-trigger evaluation.
     if (EvaluatedStmt *Eval = VD->getEvaluatedStmt()) {
       Eval->WasEvaluated = false;
@@ -1040,13 +1076,11 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
   // redeclaration chain (`enum class E : int;` forward-declared, then
   // defined later), so every redecl's cached pointer needs checking.
   if (Rec.has(MutationType::TypeForDecl)) {
-    for (const TagDecl *Redecl : ED->redecls()) {
-      const Type *LastKnown =
-          Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
-      const TypeDecl *TD = cast<TypeDecl>(Redecl);
-      if (LastKnown != TD->getTypeForDecl())
-        const_cast<TypeDecl *>(TD)->setTypeForDecl(LastKnown);
-    }
+    // for (const TagDecl *Redecl : ED->redecls()) {
+    //   const Type *LastKnown =
+    //       Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+    //   TODO: need to handle?
+    // }
   }
 
   if (Rec.has(MutationType::DefinitionInstantiate))
@@ -1067,28 +1101,17 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
 void PTUMutationActions::restoreDecl(PTUID ID, const Decl *D,
                                      MutationRecord &Rec) {
   switch (Rec.S) {
-  case DeclShape::Class:
-    restoreClass(ID, cast<CXXRecordDecl>(D), Rec);
-    break;
-  case DeclShape::Function:
-    restoreFunction(ID, cast<FunctionDecl>(D), Rec);
-    break;
-  case DeclShape::Var:
-    restoreVar(ID, cast<VarDecl>(D), Rec);
-    break;
-  case DeclShape::Enum:
-    restoreEnum(ID, cast<EnumDecl>(D), Rec);
-    break;
-  case DeclShape::Template:
-    restoreTemplate(ID, cast<RedeclarableTemplateDecl>(D), Rec);
-    break;
-  case DeclShape::Typedef:
-    restoreTypedef(ID, cast<TypedefNameDecl>(D), Rec);
+#define PTU_SHAPE(NAME, TYPE)                                                  \
+  case DeclShape::NAME:                                                        \
+    restore##NAME(ID, cast<TYPE>(D), Rec);                                     \
     break;
+    PTU_SHAPES
+#undef PTU_SHAPE
   case DeclShape::None:
     break;
   }
 }
+#undef PTU_SHAPES
 
 void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
   PTUStateInfo &Cur = Tracker.current();
@@ -1110,13 +1133,12 @@ void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
     restoreDecl(ID, D, Rec);
 
   DeclStateReverter Detacher(Tracker.getPTUSlabCheckpoints());
-  DeclStateUnlinkPolicy Proxy(ID, Tracker, Detacher);
+  DeclStateUnlinkPolicy Policy(ID, Tracker, Detacher);
 
   for (const Decl *D : Cur.ImplicitDecls)
-    if (auto A = Proxy.actionFor(D))
-      Proxy.runAction(D, A);
+    Policy.process(D);
 
-  walkDecls(ThisTU, Proxy);
+  walkDecls(ThisTU, Policy);
 
   // Sema::SpecialMemberCache (public) caches, per (RD, kind+qualifiers), the
   // CXXMethodDecl* a prior LookupSpecialMember() call resolved to -- and on

>From 4e00a3208793cdb973be7a2633a14f639523f75d Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Fri, 25 Sep 2026 12:32:10 +0530
Subject: [PATCH 09/13] Improve: Add FootprintStore removal logic and update
 implementation

---
 .../include/clang/Interpreter/ErrorRecovery.h | 600 +++---------------
 clang/lib/Interpreter/ErrRecovery.cpp         | 483 +++++++++-----
 2 files changed, 426 insertions(+), 657 deletions(-)

diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 4569494a3b2e2..2c662e9e53017 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -17,6 +17,7 @@
 #include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
+#include <variant>
 
 namespace clang {
 class ASTContext;
@@ -264,8 +265,14 @@ class DeclStateReverter {
   }
 
 protected:
-  static DefinitionDataFootprint *
-  createDefinitionDataFootprint(const ASTContext &Ctx, const CXXRecordDecl &RD);
+  // These all return by value now, not an ASTContext-arena-allocated
+  // pointer: footprints live inside FootprintStore's own SmallVector, which
+  // owns them with ordinary C++ lifetime (ctor/dtor/copy), so there's
+  // nothing left for arena allocation to buy -- the arena's job was only
+  // ever to make these survive without anyone having to free them, and a
+  // normally-owned value does that for free too.
+  static DefinitionDataFootprint
+  createDefinitionDataFootprint(const CXXRecordDecl &RD);
 
   static bool compareDefinitionDataFootprint(const DefinitionDataFootprint &FP,
                                              const CXXRecordDecl &RD);
@@ -273,12 +280,10 @@ class DeclStateReverter {
   static void restoreDefinitionDataFootprint(const DefinitionDataFootprint &FP,
                                              CXXRecordDecl &RD);
 
-  static SpecializationFootprint *
-  createSpecializationFootprint(const ASTContext &Ctx,
-                                const ClassTemplateSpecializationDecl &Spec) {
-    auto *FP =
-        new (Ctx, alignof(SpecializationFootprint)) SpecializationFootprint();
-    FP->update(Spec);
+  static SpecializationFootprint
+  createSpecializationFootprint(const ClassTemplateSpecializationDecl &Spec) {
+    SpecializationFootprint FP;
+    FP.update(Spec);
     return FP;
   }
   static bool
@@ -294,12 +299,10 @@ class DeclStateReverter {
     FP.restore(Spec);
   }
 
-  static VarSpecializationFootprint *
-  createVarSpecializationFootprint(const ASTContext &Ctx,
-                                   const VarTemplateSpecializationDecl &Spec) {
-    auto *FP = new (Ctx, alignof(VarSpecializationFootprint))
-        VarSpecializationFootprint();
-    FP->update(Spec);
+  static VarSpecializationFootprint
+  createVarSpecializationFootprint(const VarTemplateSpecializationDecl &Spec) {
+    VarSpecializationFootprint FP;
+    FP.update(Spec);
     return FP;
   }
   static bool
@@ -315,12 +318,10 @@ class DeclStateReverter {
     FP.restore(Spec);
   }
 
-  static FunctionSpecializationFootprint *
-  createFunctionSpecializationFootprint(const ASTContext &Ctx,
-                                        const FunctionDecl &FD) {
-    auto *FP = new (Ctx, alignof(FunctionSpecializationFootprint))
-        FunctionSpecializationFootprint();
-    FP->update(FD);
+  static FunctionSpecializationFootprint
+  createFunctionSpecializationFootprint(const FunctionDecl &FD) {
+    FunctionSpecializationFootprint FP;
+    FP.update(FD);
     return FP;
   }
   static bool compareFunctionSpecializationFootprint(
@@ -335,11 +336,10 @@ class DeclStateReverter {
   }
 
   template <typename OwnerT>
-  static MemberSpecializationFootprint *
-  createMemberSpecializationFootprint(const ASTContext &Ctx, const OwnerT &D) {
-    auto *FP = new (Ctx, alignof(MemberSpecializationFootprint))
-        MemberSpecializationFootprint();
-    FP->update(D);
+  static MemberSpecializationFootprint
+  createMemberSpecializationFootprint(const OwnerT &D) {
+    MemberSpecializationFootprint FP;
+    FP.update(D);
     return FP;
   }
   template <typename OwnerT>
@@ -455,60 +455,6 @@ class DeclStateReverter {
   NamedDecl *tryDetachRedeclChain(Decl *D, PTUID ID);
 };
 
-template <typename DataT> struct Snapshot {
-  PTUID ID;
-  DataT *Data;
-};
-
-template <typename DataT> class StateAwareChain {
-  llvm::SmallVector<Snapshot<DataT>, 4> History;
-
-public:
-  bool empty() const { return History.empty(); }
-
-  const DataT *mostRecent() const {
-    return History.empty() ? nullptr : History.back().Data;
-  }
-
-  std::optional<PTUID> mostRecentID() const {
-    return History.empty() ? std::nullopt
-                           : std::optional<PTUID>(History.back().ID);
-  }
-
-  std::optional<PTUID> oldestID() const {
-    return History.empty() ? std::nullopt
-                           : std::optional<PTUID>(History.front().ID);
-  }
-
-  void commit(PTUID ID, DataT *Fresh) {
-    if (!History.empty() && *History.back().Data == *Fresh)
-      return;
-    assert(History.back().ID != ID);
-    History.push_back(Snapshot<DataT>{ID, Fresh});
-  }
-
-  // const DataT *getPrevious(PTUID ID) const {
-  //   for (auto It = History.rbegin(); It != History.rend(); ++It)
-  //     if (It->ID < ID)
-  //       return It->Data;
-  //   return nullptr;
-  // }
-
-  /// removal: drop every entry with ID >= \p ID.
-  void removeFrom(PTUID ID) {
-    while (!History.empty() && History.back().ID >= ID)
-      History.pop_back(); // no delete.
-  }
-};
-
-using RecordDeclDefinitionDataChain = StateAwareChain<DefinitionDataFootprint>;
-using SpecializationChain = StateAwareChain<SpecializationFootprint>;
-using VarSpecializationChain = StateAwareChain<VarSpecializationFootprint>;
-using FunctionSpecializationChain =
-    StateAwareChain<FunctionSpecializationFootprint>;
-using MemberSpecializationChain =
-    StateAwareChain<MemberSpecializationFootprint>;
-
 /// Maps addresses to the PTU that allocated them, by keeping the slab
 /// checkpoint taken before each PTU began.
 ///
@@ -564,89 +510,6 @@ class PTUCheckpointLedger {
   }
 };
 
-template <typename ValueT> struct FieldMutation {
-  PTUID ID;
-  ValueT OldValue;
-};
-
-template <typename OwnerT, typename ValueT> class FieldMutationChain {
-  llvm::DenseMap<const OwnerT *, llvm::SmallVector<FieldMutation<ValueT>, 2>>
-      Log;
-
-public:
-  void noteMutation(PTUID ID, const OwnerT *Owner, ValueT OldValue) {
-    auto &Entries = Log[Owner];
-    if (!Entries.empty() && Entries.back().ID == ID)
-      return;
-    Entries.push_back(FieldMutation<ValueT>{ID, OldValue});
-  }
-
-  std::optional<ValueT> mostRecent(const OwnerT *Owner) const {
-    auto It = Log.find(Owner);
-    if (It == Log.end() || It->second.empty())
-      return std::nullopt;
-    return It->second.back().OldValue;
-  }
-
-  std::optional<PTUID> mostRecentID(const OwnerT *Owner) const {
-    auto It = Log.find(Owner);
-    if (It == Log.end())
-      return std::nullopt;
-    auto &History = It->second;
-    return History.empty() ? std::nullopt
-                           : std::optional<PTUID>(History.back().ID);
-  }
-
-  std::optional<PTUID> oldestID(const OwnerT *Owner) const {
-    auto It = Log.find(Owner);
-    if (It == Log.end())
-      return std::nullopt;
-    auto &History = It->second;
-    return History.empty() ? std::nullopt
-                           : std::optional<PTUID>(History.front().ID);
-  }
-
-  // template <typename FnT> void forEachOwnerSince(PTUID ID, FnT &&Fn) const {
-  //   for (auto &Entry : Log)
-  //     if (!Entry.second.empty() && Entry.second.back().ID >= ID)
-  //       Fn(Entry.first);
-  // }
-
-  // void removeFrom(PTUID ID) {
-  //   llvm::SmallVector<OwnerT *> ToErase;
-  //   for (auto &Entry : Log) {
-  //     auto &Entries = Entry.second;
-  //     while (!Entries.empty() && Entries.back().ID >= ID)
-  //       Entries.pop_back();
-  //     if (Entries.empty())
-  //       ToErase.push_back(Entry.first);
-  //   }
-  //   for (const OwnerT *O : ToErase)
-  //     Log.erase(O);
-  // }
-
-  /// Same trim-then-erase-if-empty as removeFrom(ID), scoped to one owner
-  /// instead of sweeping the whole Log -- for callers that already know
-  /// exactly which owners this PTU touched (see PTUStateInfo's
-  /// TouchedFunctionTypeOwners/TouchedTagdeclOwners) and don't need to
-  /// rediscover that by walking every owner ever logged.
-  void removeFromOwner(const OwnerT *Owner, PTUID ID) {
-    auto It = Log.find(Owner);
-    if (It == Log.end())
-      return;
-    auto &Entries = It->second;
-    while (!Entries.empty() && Entries.back().ID >= ID)
-      Entries.pop_back();
-    if (Entries.empty())
-      Log.erase(It);
-  }
-
-  void forget(const OwnerT *Owner) { Log.erase(Owner); }
-};
-
-using FunctionExceptionSpecChain = FieldMutationChain<FunctionDecl, QualType>;
-using TypeForDeclChain = FieldMutationChain<TagDecl, const Type *>;
-
 struct MutationRecord {
   enum class DeclShape : uint16_t {
     None = 0,
@@ -690,6 +553,58 @@ struct MutationRecord {
 using DeclShape = MutationRecord::DeclShape;
 using MutationType = MutationRecord::MutationKind;
 
+using FootprintValue =
+    std::variant<DefinitionDataFootprint, SpecializationFootprint,
+                 VarSpecializationFootprint, FunctionSpecializationFootprint,
+                 MemberSpecializationFootprint, QualType, const Type *>;
+
+struct FootprintSnapshot {
+  PTUID ID;
+  FootprintValue Data;
+};
+
+class FootprintStore {
+  llvm::DenseMap<std::pair<const Decl *, MutationType>,
+                 llvm::SmallVector<FootprintSnapshot, 2>>
+      Store;
+
+public:
+  // Same dedup StateAwareChain::commit() always had: re-committing the
+  // same value for the same owner+kind is a no-op, not a new history entry.
+  template <typename T>
+  void commit(const Decl *Owner, MutationType K, PTUID ID, const T &Fresh) {
+    auto &History = Store[{Owner, K}];
+    if (!History.empty() && std::get<T>(History.back().Data) == Fresh)
+      return;
+    History.push_back(FootprintSnapshot{ID, FootprintValue(Fresh)});
+  }
+
+  // nullptr means either "no history at all" or "history exists but the
+  // most recent entry isn't a T" -- the latter never legitimately happens
+  // for a given (Owner, K) pair, since K determines which alternative was
+  // ever committed there.
+  template <typename T>
+  const T *mostRecent(const Decl *Owner, MutationType K) const {
+    auto It = Store.find({Owner, K});
+    if (It == Store.end() || It->second.empty())
+      return nullptr;
+    return std::get_if<T>(&It->second.back().Data);
+  }
+
+  /// Drop every entry with ID >= \p ID for this (Owner, K), erasing the
+  /// key entirely once its history is empty.
+  void removeFrom(const Decl *Owner, MutationType K, PTUID ID) {
+    auto It = Store.find({Owner, K});
+    if (It == Store.end())
+      return;
+    auto &History = It->second;
+    while (!History.empty() && History.back().ID >= ID)
+      History.pop_back();
+    if (History.empty())
+      Store.erase(It);
+  }
+};
+
 struct PTUStateInfo {
   PTUID ID;
   const TranslationUnitDecl *ThisPTU; // current info
@@ -701,13 +616,13 @@ struct PTUStateInfo {
   /// here touched info mean other this belongs to other PTUs;
   llvm::SmallPtrSet<const DeclContext *, 4> TouchedDC;
 
-  /// Exactly the decls this PTU's own commit() pushed a footprint-chain
-  /// entry for (DefinitionData/Specialization/MemberSpecialization, via
-  /// chainFor()/memberSpecChainFor()).
-  llvm::SmallPtrSet<const Decl *, 8> TouchedFootprintDecls;
-
-  llvm::SmallPtrSet<const FunctionDecl *, 4> TouchedFunctionTypeOwners;
-  llvm::SmallPtrSet<const TagDecl *, 4> TouchedTagdeclOwners;
+  /// Every (Owner, Kind) key this PTU's own commit() wrote a FootprintStore
+  /// entry for -- replaces the old TouchedFootprintDecls/
+  /// TouchedFunctionTypeOwners/TouchedTagdeclOwners trio (one set per
+  /// container that used to exist) with one list, since there's now only
+  /// one container. Duplicates are harmless: FootprintStore::removeFrom is
+  /// idempotent.
+  llvm::SmallVector<std::pair<const Decl *, MutationType>, 8> TouchedFootprints;
 
   explicit PTUStateInfo(PTUID ID) : ID(ID) {}
 
@@ -779,137 +694,10 @@ class SweepTracker {
   void sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed);
 };
 
-//===----------------------------------------------------------------------===//
-// DeclLinkedState / LinkedDeclNodeGenerator
-//
-// These nodes keep the footprint-chain state associated with a Decl.
-// Each Decl kind has its own node type so the node only contains state that
-// is valid for that kind. DeclLinkedState then uses the node type to identify
-// which kind of Decl it belongs to.
-//
-// The generator owns these nodes and their chains and reuses them through
-// per-type free lists. This is intentional: nodes need to be released and
-// reused, so they use normal heap allocation instead of ASTContext's
-// bump allocator.
-//
-// The goal is to keep the linked state small, type-safe, and reusable without
-// adding fields for states that a particular Decl can never have.
-//===----------------------------------------------------------------------===//
-struct CXXClassDeclNode {
-  PTUID OriginID;
-  RecordDeclDefinitionDataChain *DefData = nullptr;
-  llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *> Spec;
-};
-
-struct VarDeclNode {
-  PTUID OriginID;
-  llvm::PointerUnion<VarSpecializationChain *, MemberSpecializationChain *>
-      Spec;
-};
-
-struct FunctionDeclNode {
-  PTUID OriginID;
-  llvm::PointerUnion<FunctionSpecializationChain *, MemberSpecializationChain *>
-      Spec;
-};
-
-struct EnumDeclNode {
-  PTUID OriginID;
-  MemberSpecializationChain *MemberSpec =
-      nullptr; // the only thing an enum can ever have
-};
-
-using DeclLinkedState = llvm::PointerUnion<CXXClassDeclNode *, VarDeclNode *,
-                                           FunctionDeclNode *, EnumDeclNode *>;
-
-/// Pool for reusing node/chain objects without requiring any intrusive
-/// free-list field on T itself -- freed pointers are tracked in a side
-/// vector instead, so this works for the four node types above and the
-/// five StateAwareChain-based chain types alike. Released objects are kept
-/// for reuse, and the pool is bounded by Capacity so unused objects do not
-/// cause unbounded memory growth.
-template <typename T> class ObjectPool {
-  llvm::SmallVector<T *, 8> Free;
-  unsigned Capacity;
-
-public:
-  explicit ObjectPool(unsigned Capacity = 64) : Capacity(Capacity) {}
-
-  ~ObjectPool() {
-    for (T *C : Free)
-      delete C;
-  }
-
-  T *acquire() {
-    if (!Free.empty()) {
-      T *C = Free.pop_back_val();
-      *C = T();
-      return C;
-    }
-    return new T();
-  }
-  /// Reclaimed for real once Free is at capacity, rather than growing
-  /// without bound.
-  void release(T *C) {
-    if (Free.size() >= Capacity) {
-      delete C;
-      return;
-    }
-    Free.push_back(C);
-  }
-};
-
-class LinkedDeclNodeGenerator {
-  ObjectPool<CXXClassDeclNode> RecordNodes;
-  ObjectPool<VarDeclNode> VarNodes;
-  ObjectPool<FunctionDeclNode> FunctionNodes;
-  ObjectPool<EnumDeclNode> EnumNodes;
-
-  ObjectPool<RecordDeclDefinitionDataChain> DefDataChains;
-  ObjectPool<SpecializationChain> ClassSpecChains;
-  ObjectPool<VarSpecializationChain> VarSpecChains;
-  ObjectPool<FunctionSpecializationChain> FunctionSpecChains;
-  ObjectPool<MemberSpecializationChain> MemberSpecChains;
-
-public:
-  CXXClassDeclNode *acquireRecordNode() { return RecordNodes.acquire(); }
-  VarDeclNode *acquireVarNode() { return VarNodes.acquire(); }
-  FunctionDeclNode *acquireFunctionNode() { return FunctionNodes.acquire(); }
-  EnumDeclNode *acquireEnumNode() { return EnumNodes.acquire(); }
-
-  RecordDeclDefinitionDataChain *acquireDefDataChain() {
-    return DefDataChains.acquire();
-  }
-  SpecializationChain *acquireClassSpecChain() {
-    return ClassSpecChains.acquire();
-  }
-  VarSpecializationChain *acquireVarSpecChain() {
-    return VarSpecChains.acquire();
-  }
-  FunctionSpecializationChain *acquireFunctionSpecChain() {
-    return FunctionSpecChains.acquire();
-  }
-  MemberSpecializationChain *acquireMemberSpecChain() {
-    return MemberSpecChains.acquire();
-  }
-
-  void release(CXXClassDeclNode *N) { RecordNodes.release(N); }
-  void release(VarDeclNode *N) { VarNodes.release(N); }
-  void release(FunctionDeclNode *N) { FunctionNodes.release(N); }
-  void release(EnumDeclNode *N) { EnumNodes.release(N); }
-  void release(RecordDeclDefinitionDataChain *C) { DefDataChains.release(C); }
-  void release(SpecializationChain *C) { ClassSpecChains.release(C); }
-  void release(VarSpecializationChain *C) { VarSpecChains.release(C); }
-  void release(FunctionSpecializationChain *C) {
-    FunctionSpecChains.release(C);
-  }
-  void release(MemberSpecializationChain *C) { MemberSpecChains.release(C); }
-};
-
-enum class LangMode : uint8_t {
-  C,   // no DefinitionData, no templates, no implicit special members
-  CXX, // full set
-};
+// enum class LangMode : uint8_t {
+//   C,   // no DefinitionData, no templates, no implicit special members
+//   CXX, // full set
+// };
 
 // Overall flow:
 // - Track changes made by other PTUs so they can be applied to the relevant
@@ -930,197 +718,36 @@ class IncrementalStateTracker {
   // Sema::SpecialMemberCache entries for decls this PTU created.
   Sema &SemaRef;
   PTUCheckpointLedger PTUSlabCheckpoints;
-  LangMode Mode;
   PTUID NextID = 0;
 
   mutable std::vector<PTUStateInfo> PTUStack;
 
-  // Stores all declaration footprint state in one map instead of maintaining
-  // separate maps for each declaration/specialization kind. The generator
-  // creates the appropriate node and chain for each Decl.
-  mutable LinkedDeclNodeGenerator Generator;
-  mutable llvm::DenseMap<const Decl *, DeclLinkedState> LinkedDecls;
+  // One relation for every tracked footprint/value kind.
+  FootprintStore Footprints;
 
   // Tracks hidden mutations for all supported Decl kinds in one shared tracker,
   // so they can be restored correctly during rollback.
   SweepTracker HiddenMutationTracker;
 
-  FunctionExceptionSpecChain FunctionTypeMutations;
-  TypeForDeclChain TagDeclTypes;
-
   friend class PTUMutationActions;
 
-  void noteFunctionTypeMutation(PTUID ID, const FunctionDecl *Owner,
-                                QualType OldValue) {
-    FunctionTypeMutations.noteMutation(ID, Owner, OldValue);
-    current().TouchedFunctionTypeOwners.insert(Owner);
-  }
-  void noteTagDeclTypeMutation(PTUID ID, const TagDecl *Owner,
-                               const Type *OldValue) {
-    TagDeclTypes.noteMutation(ID, Owner, OldValue);
-    current().TouchedTagdeclOwners.insert(Owner);
-  }
-
-  TypeForDeclChain &getTagDeclTypeInfo() { return TagDeclTypes; }
-  FunctionExceptionSpecChain &getFunctionTypeMutations() {
-    return FunctionTypeMutations;
-  }
-
-  /// Get-or-create the CXXClassDeclNode backing Canon, acquiring one from
-  /// the generator on first use. The only place a CXXClassDeclNode is
-  /// created for this map.
-  template <typename NodeT, typename AcquireFn>
-  NodeT &nodeFor(const Decl *D, AcquireFn Acquire) {
-    current().TouchedFootprintDecls.insert(D);
-    DeclLinkedState &Info = LinkedDecls[D];
-    if (Info.isNull())
-      Info = (Generator.*Acquire)();
-    return *cast<NodeT *>(Info);
-  }
-
-  template <typename NodeT> NodeT *lookupNode(const Decl *D) const {
-    auto It = LinkedDecls.find(D);
-    if (It == LinkedDecls.end() || It->second.isNull())
-      return nullptr;
-    return dyn_cast<NodeT *>(It->second);
-  }
-
-  CXXClassDeclNode &recordNodeFor(const CXXRecordDecl *RD) {
-    return nodeFor<CXXClassDeclNode>(
-        RD, &LinkedDeclNodeGenerator::acquireRecordNode);
-  }
-  VarDeclNode &varNodeFor(const VarDecl *VD) {
-    return nodeFor<VarDeclNode>(VD, &LinkedDeclNodeGenerator::acquireVarNode);
-  }
-  FunctionDeclNode &functionNodeFor(const FunctionDecl *FD) {
-    return nodeFor<FunctionDeclNode>(
-        FD, &LinkedDeclNodeGenerator::acquireFunctionNode);
-  }
-  EnumDeclNode &enumNodeFor(const EnumDecl *ED) {
-    return nodeFor<EnumDeclNode>(ED, &LinkedDeclNodeGenerator::acquireEnumNode);
-  }
-
-  template <typename WantT, typename NodeT, typename AcquireFn>
-  WantT &specChain(NodeT &Node, AcquireFn Acquire) {
-    if (auto *C = Node.Spec.template dyn_cast<WantT *>())
-      return *C;
-    assert(Node.Spec.isNull() && "node already holds the other Spec kind");
-    auto *C = (Generator.*Acquire)();
-    Node.Spec = C;
-    return *C;
-  }
-
-  RecordDeclDefinitionDataChain &chainFor(const CXXRecordDecl *RD) {
-    CXXClassDeclNode &Node = recordNodeFor(RD);
-    if (!Node.DefData)
-      Node.DefData = Generator.acquireDefDataChain();
-    return *Node.DefData;
-  }
-
-  SpecializationChain &chainFor(const ClassTemplateSpecializationDecl *S) {
-    return specChain<SpecializationChain>(
-        recordNodeFor(cast<CXXRecordDecl>(S)),
-        &LinkedDeclNodeGenerator::acquireClassSpecChain);
-  }
-
-  VarSpecializationChain &chainFor(const VarTemplateSpecializationDecl *S) {
-    return specChain<VarSpecializationChain>(
-        varNodeFor(cast<VarDecl>(S)),
-        &LinkedDeclNodeGenerator::acquireVarSpecChain);
-  }
-
-  FunctionSpecializationChain &chainFor(const FunctionDecl *S) {
-    return specChain<FunctionSpecializationChain>(
-        functionNodeFor(cast<FunctionDecl>(S)),
-        &LinkedDeclNodeGenerator::acquireFunctionSpecChain);
-  }
-
-  MemberSpecializationChain &memberSpecChainFor(const FunctionDecl *FD) {
-    FunctionDeclNode &Node = functionNodeFor(FD);
-    auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
-    if (!Chain) {
-      Chain = Generator.acquireMemberSpecChain();
-      Node.Spec = Chain;
-    }
-    return *Chain;
-  }
-  MemberSpecializationChain &memberSpecChainFor(const VarDecl *VD) {
-    VarDeclNode &Node = varNodeFor(VD);
-    auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
-    if (!Chain) {
-      Chain = Generator.acquireMemberSpecChain();
-      Node.Spec = Chain;
-    }
-    return *Chain;
-  }
-  MemberSpecializationChain &memberSpecChainFor(const CXXRecordDecl *RD) {
-    CXXClassDeclNode &Node = recordNodeFor(RD);
-    auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
-    if (!Chain) {
-      Chain = Generator.acquireMemberSpecChain();
-      Node.Spec = Chain;
-    }
-    return *Chain;
-  }
-  MemberSpecializationChain &memberSpecChainFor(const EnumDecl *ED) {
-    EnumDeclNode &Node = enumNodeFor(ED);
-    if (!Node.MemberSpec)
-      Node.MemberSpec = Generator.acquireMemberSpecChain();
-    return *Node.MemberSpec;
-  }
-
-  const RecordDeclDefinitionDataChain *
-  getChainFor(const CXXRecordDecl *RD) const {
-    CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(RD);
-    return N ? N->DefData : nullptr;
-  }
-  const SpecializationChain *
-  getChainFor(const ClassTemplateSpecializationDecl *S) const {
-    CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(S);
-    return N ? N->Spec.dyn_cast<SpecializationChain *>() : nullptr;
-  }
-
-  const VarSpecializationChain *
-  getChainFor(const VarTemplateSpecializationDecl *Spec) const {
-    VarDeclNode *N = lookupNode<VarDeclNode>(Spec);
-    return N ? N->Spec.dyn_cast<VarSpecializationChain *>() : nullptr;
-  }
-
-  const FunctionSpecializationChain *
-  getChainFor(const FunctionDecl *Spec) const {
-    FunctionDeclNode *N = lookupNode<FunctionDeclNode>(Spec);
-    return N ? N->Spec.dyn_cast<FunctionSpecializationChain *>() : nullptr;
-  }
-
-  const MemberSpecializationChain *
-  getMemberSpecChainFor(const FunctionDecl *FD) const {
-    FunctionDeclNode *N = lookupNode<FunctionDeclNode>(FD);
-    return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
-  }
-
-  const MemberSpecializationChain *
-  getMemberSpecChainFor(const VarDecl *VD) const {
-    VarDeclNode *N = lookupNode<VarDeclNode>(VD);
-    return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
-  }
-
-  const MemberSpecializationChain *
-  getMemberSpecChainFor(const CXXRecordDecl *RD) const {
-    CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(RD);
-    return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
-  }
+public:
+  IncrementalStateTracker(ASTContext &Ctx, Sema &S)
+      : Ctx(Ctx), SemaRef(S), PTUSlabCheckpoints(Ctx) {}
 
-  const MemberSpecializationChain *
-  getMemberSpecChainFor(const EnumDecl *ED) const {
-    EnumDeclNode *N = lookupNode<EnumDeclNode>(ED);
-    return N ? N->MemberSpec : nullptr;
+  // Writes a new value and records that this PTU touched (Owner, K), so
+  // undoLastEntries() knows what to remove if the PTU is rolled back
+  // without committing. mostRecent() doesn't need any bookkeeping, so it
+  // reads directly from Footprints via Tracker.Footprints.mostRecent<T>(...)
+  // instead of having its own wrapper.
+  template <typename T>
+  void commitFootprint(const Decl *Owner, MutationType K, PTUID ID,
+                       const T &Fresh) {
+    Footprints.commit(Owner, K, ID, Fresh);
+    current().TouchedFootprints.emplace_back(Owner, K);
   }
 
-public:
-  IncrementalStateTracker(ASTContext &Ctx, Sema &S, LangMode M)
-      : Ctx(Ctx), SemaRef(S), PTUSlabCheckpoints(Ctx), Mode(M) {}
-
-  LangMode getMode() const { return Mode; }
+  FootprintStore &getFootprints() { return Footprints; }
 
   PTUStateInfo &current() const {
     assert(!PTUStack.empty() && "no PTU has been started");
@@ -1156,25 +783,6 @@ class IncrementalStateTracker {
     PTUSlabCheckpoints.undoFrom(ID);
     NextID = ID;
   }
-
-  template <typename ChainT>
-  static bool removeChainField(ChainT *&Field, PTUID ID,
-                               LinkedDeclNodeGenerator &Gen);
-
-  static bool removeRecordSpec(
-      llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *>
-          &Spec,
-      PTUID ID, LinkedDeclNodeGenerator &Gen);
-  static bool removeVarSpec(
-      llvm::PointerUnion<VarSpecializationChain *, MemberSpecializationChain *>
-          &Spec,
-      PTUID ID, LinkedDeclNodeGenerator &Gen);
-  static bool
-  removeFunctionSpec(llvm::PointerUnion<FunctionSpecializationChain *,
-                                        MemberSpecializationChain *> &Spec,
-                     PTUID ID, LinkedDeclNodeGenerator &Gen);
-  void removeLinkedDecl(const Decl *D, PTUID ID);
-  // just only remove entries from current();
 };
 
 class PTUMutationActions {
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index 216d896f498bd..5c61e0165507f 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -33,13 +33,11 @@ namespace clang {
 //////////////////////// PTUMutationActions::Helpers ///////////////////////
 /// -----------------------------------------------------------------------
 
-DefinitionDataFootprint *
-DeclStateReverter::createDefinitionDataFootprint(const ASTContext &Ctx,
-                                                 const CXXRecordDecl &RD) {
-  auto *FP =
-      new (Ctx, alignof(DefinitionDataFootprint)) DefinitionDataFootprint();
+DefinitionDataFootprint
+DeclStateReverter::createDefinitionDataFootprint(const CXXRecordDecl &RD) {
+  DefinitionDataFootprint FP;
   const auto &Live = RD.data();
-#define FIELD(Name, Width, Merge) FP->Name = Live.Name;
+#define FIELD(Name, Width, Merge) FP.Name = Live.Name;
 #include "clang/AST/CXXRecordDeclDefinitionBits.def"
   return FP;
 }
@@ -235,39 +233,16 @@ NamedDecl *DeclStateReverter::tryDetachRedeclChain(Decl *D, PTUID ID) {
     Survivor = SurvivorDecl;
   };
 
-  switch (D->getKind()) {
-  case Decl::Function:
-    unlinkRedeclChain(cast<FunctionDecl>(D));
-    break;
-
-  case Decl::Var:
-    unlinkRedeclChain(cast<VarDecl>(D));
-    break;
-
-  case Decl::Enum:
-  case Decl::Record:
-  case Decl::CXXRecord:
-    unlinkRedeclChain(cast<TagDecl>(D));
-    break;
-
-  case Decl::ClassTemplate: {
-    // NamedDecl *ND = cast<RedeclarableTemplateDecl>(D)->getTemplatedDecl();
-    unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
-    // tryDetachRedeclChain(ND);
-  } break;
-
-  case Decl::FunctionTemplate:
-  case Decl::TypeAliasTemplate:
-  case Decl::VarTemplate:
-    unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
-    break;
-  case Decl::Namespace:
-    unlinkRedeclChain(cast<NamespaceDecl>(D));
-    break;
-
-  default:
-    break;
-  }
+  if (auto *FD = dyn_cast<FunctionDecl>(D))
+    unlinkRedeclChain(FD);
+  else if (auto *VD = dyn_cast<VarDecl>(D))
+    unlinkRedeclChain(VD);
+  else if (auto *TD = dyn_cast<TagDecl>(D))
+    unlinkRedeclChain(TD);
+  else if (auto *RT = dyn_cast<RedeclarableTemplateDecl>(D))
+    unlinkRedeclChain(RT);
+  else if (auto *NS = dyn_cast<NamespaceDecl>(D))
+    unlinkRedeclChain(NS);
 
   return Survivor;
 }
@@ -492,25 +467,28 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
     Verified |= FlaggedKinds & uint32_t(MutationType::DefinitionData);
     if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
       //   const Type *Now = DeclStateReverter::getRawTypeForDecl(RD);
-      //   if (Tracker.getTagDeclTypeInfo().mostRecent(RD).value_or(nullptr) !=
-      //   Now)
+      //   const auto *Last =
+      //       Tracker.Footprints.mostRecent<const Type *>(RD,
+      //       MutationType::TypeForDecl);
+      //   assert(Last && "TypeForDecl baseline missing at verify time");
+      //   if (!Last || *Last != Now)
       //     Verified |= uint32_t(MutationType::TypeForDecl);
     }
     if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
       if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
-        auto *Chain = Tracker.getChainFor(Spec);
-        assert(Chain && "SpecInfo baseline missing at verify time");
-        const SpecializationFootprint *Last =
-            Chain ? Chain->mostRecent() : nullptr;
+        const auto *Last =
+            Tracker.Footprints.mostRecent<SpecializationFootprint>(
+                Spec, MutationType::SpecInfo);
+        assert(Last && "SpecInfo baseline missing at verify time");
         if (!Last ||
             !DeclStateReverter::compareSpecializationFootprint(*Last, *Spec))
           Verified |= uint32_t(MutationType::SpecInfo);
       }
     } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
-      auto *Chain = Tracker.getMemberSpecChainFor(RD);
-      assert(Chain && "MemberSpecInfo baseline missing at verify time");
-      const MemberSpecializationFootprint *Last =
-          Chain ? Chain->mostRecent() : nullptr;
+      const auto *Last =
+          Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+              RD, MutationType::MemberSpecInfo);
+      assert(Last && "MemberSpecInfo baseline missing at verify time");
       if (!Last ||
           !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *RD))
         Verified |= uint32_t(MutationType::MemberSpecInfo);
@@ -520,18 +498,18 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
   case DeclShape::Function: {
     const auto *FD = cast<FunctionDecl>(D);
     if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
-      auto *Chain = Tracker.getChainFor(FD);
-      assert(Chain && "SpecInfo baseline missing at verify time");
-      const FunctionSpecializationFootprint *Last =
-          Chain ? Chain->mostRecent() : nullptr;
+      const auto *Last =
+          Tracker.Footprints.mostRecent<FunctionSpecializationFootprint>(
+              FD, MutationType::SpecInfo);
+      assert(Last && "SpecInfo baseline missing at verify time");
       if (!Last || !DeclStateReverter::compareFunctionSpecializationFootprint(
                        *Last, *FD))
         Verified |= uint32_t(MutationType::SpecInfo);
     } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
-      auto *Chain = Tracker.getMemberSpecChainFor(FD);
-      assert(Chain && "MemberSpecInfo baseline missing at verify time");
-      const MemberSpecializationFootprint *Last =
-          Chain ? Chain->mostRecent() : nullptr;
+      const auto *Last =
+          Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+              FD, MutationType::MemberSpecInfo);
+      assert(Last && "MemberSpecInfo baseline missing at verify time");
       if (!Last ||
           !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *FD))
         Verified |= uint32_t(MutationType::MemberSpecInfo);
@@ -546,19 +524,19 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
     const auto *VD = cast<VarDecl>(D);
     if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
       if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
-        auto *Chain = Tracker.getChainFor(Spec);
-        assert(Chain && "SpecInfo baseline missing at verify time");
-        const VarSpecializationFootprint *Last =
-            Chain ? Chain->mostRecent() : nullptr;
+        const auto *Last =
+            Tracker.Footprints.mostRecent<VarSpecializationFootprint>(
+                Spec, MutationType::SpecInfo);
+        assert(Last && "SpecInfo baseline missing at verify time");
         if (!Last ||
             !DeclStateReverter::compareVarSpecializationFootprint(*Last, *Spec))
           Verified |= uint32_t(MutationType::SpecInfo);
       }
     } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
-      auto *Chain = Tracker.getMemberSpecChainFor(VD);
-      assert(Chain && "MemberSpecInfo baseline missing at verify time");
-      const MemberSpecializationFootprint *Last =
-          Chain ? Chain->mostRecent() : nullptr;
+      const auto *Last =
+          Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+              VD, MutationType::MemberSpecInfo);
+      assert(Last && "MemberSpecInfo baseline missing at verify time");
       if (!Last ||
           !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *VD))
         Verified |= uint32_t(MutationType::MemberSpecInfo);
@@ -578,15 +556,18 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
     const auto *ED = cast<EnumDecl>(D);
     // if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
     //   const Type *Now = DeclStateReverter::getRawTypeForDecl(ED);
-    //   if (Tracker.getTagDeclTypeInfo().mostRecent(ED).value_or(nullptr) !=
-    //   Now)
+    //   const auto *Last =
+    //       Tracker.Footprints.mostRecent<const Type *>(ED,
+    //       MutationType::TypeForDecl);
+    //   assert(Last && "TypeForDecl baseline missing at verify time");
+    //   if (!Last || *Last != Now)
     //     Verified |= uint32_t(MutationType::TypeForDecl);
     // }
     if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
-      auto *Chain = Tracker.getMemberSpecChainFor(ED);
-      assert(Chain && "MemberSpecInfo baseline missing at verify time");
-      const MemberSpecializationFootprint *Last =
-          Chain ? Chain->mostRecent() : nullptr;
+      const auto *Last =
+          Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+              ED, MutationType::MemberSpecInfo);
+      assert(Last && "MemberSpecInfo baseline missing at verify time");
       if (!Last ||
           !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *ED))
         Verified |= uint32_t(MutationType::MemberSpecInfo);
@@ -802,43 +783,38 @@ void PTUMutationActions::commitMembers(PTUID ID, const DeclContext *Members) {
 void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
                                      MutationRecord &Rec, bool IsNew) {
 
-  // TypeForDecl is shared across the whole redeclaration chain – the Type
-  // object itself is owned by whichever redecl first materialized it, so
-  // every redecl’s cached pointer needs to be checked, not just RD’s.
-  if (Rec.has(MutationType::TypeForDecl)) {
+  using ClassMutation = MutationType;
+
+  if (Rec.has(ClassMutation::TypeForDecl)) {
     // for (const TagDecl *Redecl : RD->redecls()) {
-    //   const Type *LastKnown =
-    //       Tracker.TagdeclInfos.mostRecent(Redecl).value_or(nullptr);
-    //    TODO: need to handle?
+    //   const auto *Last = Tracker.getFootprints().mostRecent<const Type *>(
+    //       Redecl, MutationType::TypeForDecl);
+    //   const Type *LastKnown = Last ? *Last : nullptr;
+    //   if (LastKnown != cast<TypeDecl>(Redecl)->getTypeForDecl())
+    //     Tracker.commitFootprint(Redecl, MutationType::TypeForDecl, ID,
+    //                             LastKnown);
     // }
   }
 
-  // DefinitionArrived and DefinitionDataChanged are committed the same way –
-  // both mean “this record’s DefinitionData needs a fresh footprint.” They
-  // remain separate kinds because rollback and dependency-edge attribution
-  // treat them differently: an arrival means this PTU completed a record
-  // that predates it, while a data change may be either.
-  if (Rec.has(MutationType::DefinitionInstantiate) ||
-      Rec.has(MutationType::DefinitionData)) {
-    Tracker.chainFor(RD).commit(
-        ID, DeclStateReverter::createDefinitionDataFootprint(Tracker.Ctx, *RD));
+  if (Rec.has(ClassMutation::DefinitionInstantiate) ||
+      Rec.has(ClassMutation::DefinitionData)) {
+    Tracker.commitFootprint(
+        RD, MutationType::DefinitionData, ID,
+        DeclStateReverter::createDefinitionDataFootprint(*RD));
 
     /// SpecializationDecl can have lazy implicit generated body;
-    if (Rec.has(MutationType::DefinitionInstantiate)) {
+    if (Rec.has(ClassMutation::DefinitionInstantiate)) {
       if (RD->getMemberSpecializationInfo())
         commitMembers(ID, cast<DeclContext>(RD));
     }
   }
 
-  if (Rec.has(MutationType::SpecInfo)) {
-    // Only a specialization can carry this kind; a plain CXXRecordDecl
-    // reaching here means a note*() wrapper passed the wrong kind.
+  if (Rec.has(ClassMutation::SpecInfo)) {
     const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD);
-    // assert(Spec && "SpecializationAdded noted on a non-specialization");
     if (Spec) {
-      Tracker.chainFor(Spec).commit(
-          ID,
-          DeclStateReverter::createSpecializationFootprint(Tracker.Ctx, *Spec));
+      Tracker.commitFootprint(
+          Spec, MutationType::SpecInfo, ID,
+          DeclStateReverter::createSpecializationFootprint(*Spec));
       // Member declarations are instantiated eagerly with the class
       // definition regardless of TSK; only their definitions defer. Walk
       // them now so each member's MemberSpecializationInfo is tracked from
@@ -848,13 +824,168 @@ void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
     }
   }
 
-  if (Rec.has(MutationType::MemberSpecInfo)) {
+  if (Rec.has(ClassMutation::MemberSpecInfo)) {
     // A nested member of a class template -- its MSI's kind or
     // point-of-instantiation changed.
     if (RD->getMemberSpecializationInfo())
-      Tracker.memberSpecChainFor(RD).commit(
-          ID, DeclStateReverter::createMemberSpecializationFootprint(
-                  Tracker.Ctx, *RD));
+      Tracker.commitFootprint(
+          RD, MutationType::MemberSpecInfo, ID,
+          DeclStateReverter::createMemberSpecializationFootprint(*RD));
+  }
+}
+
+void PTUMutationActions::commitFunction(PTUID ID, const FunctionDecl *FD,
+                                        MutationRecord &Rec, bool IsNew) {
+
+  if (Rec.has(MutationType::ExceptionSpec) ||
+      Rec.has(MutationType::DeducedReturnType)) {
+    for (const FunctionDecl *Redecl : FD->redecls()) {
+      if (IsNew) {
+        Tracker.commitFootprint(Redecl, MutationType::ExceptionSpec, ID,
+                                Redecl->getType());
+      } else {
+        const auto *Last = Tracker.Footprints.mostRecent<QualType>(
+            Redecl, MutationType::ExceptionSpec);
+        QualType LastKnown = Last ? *Last : QualType();
+        if (LastKnown != Redecl->getType())
+          Tracker.commitFootprint(Redecl, MutationType::ExceptionSpec, ID,
+                                  LastKnown);
+      }
+    }
+  }
+
+  //   if (!IsNew && Rec.has(MutationType::DefinitionInstantiate)) {
+  //   }
+
+  if (Rec.has(MutationType::SpecInfo)) {
+    assert(FD->getTemplateSpecializationKind() != TSK_Undeclared &&
+           "SpecInfoChanged on a function with no specialization info");
+    Tracker.commitFootprint(
+        FD, MutationType::SpecInfo, ID,
+        DeclStateReverter::createFunctionSpecializationFootprint(*FD));
+  }
+
+  if (Rec.has(MutationType::MemberSpecInfo)) {
+    if (FD->getMemberSpecializationInfo())
+      Tracker.commitFootprint(
+          FD, MutationType::MemberSpecInfo, ID,
+          DeclStateReverter::createMemberSpecializationFootprint(*FD));
+  }
+}
+
+void PTUMutationActions::commitVar(PTUID ID, const VarDecl *VD,
+                                   MutationRecord &Rec, bool IsNew) {
+
+  // EvaluatedStmt / APValue: populated on the FIRST constant-evaluation of
+  // this variable, which can land in any PTU long after the VarDecl was
+  // created. This is a mutation of an earlier PTU's decl, not a property
+  // settled at declaration time.
+  if (Rec.has(MutationType::EvaluatedValue)) {
+    // No footprint chain -- write-once field with a fixed null prior
+    // state (see restoreVar), so nothing to seed a baseline for.
+    if (IsNew) {
+      // Just created: register VD so a later commit's
+      // SweepTracker::sweep() call notices if/when WasEvaluated actually
+      // flips (no listener exists to tell us directly -- see
+      // hiddenKindsMask).
+      HiddenMutationTracker.track(VD, uint32_t(MutationType::EvaluatedValue));
+    } else {
+      // This IS the confirmation, arriving here via SweepTracker::sweep()'s
+      // OnConfirmed callback (there is no other path to this bit -- no
+      // listener exists for it). Write-once means terminal: once
+      // WasEvaluated is true it can never become false again on its own,
+      // so settle the bit now rather than waiting for the next sweep to
+      // notice kindsNeedingTracking no longer flags it. restoreVar
+      // is what re-track()s it, only if a rollback actually reverts this.
+      HiddenMutationTracker.settle(VD, uint32_t(MutationType::EvaluatedValue));
+    }
+  }
+
+  //   if (!IsNew && Rec.has(MutationType::DefinitionInstantiate)) {
+  //   }
+
+  if (Rec.has(MutationType::SpecInfo)) {
+    // Unlike functions, a variable specialization IS a distinct type.
+    const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
+    Tracker.commitFootprint(
+        Spec, MutationType::SpecInfo, ID,
+        DeclStateReverter::createVarSpecializationFootprint(*Spec));
+  }
+
+  if (Rec.has(MutationType::MemberSpecInfo)) {
+    if (VD->getMemberSpecializationInfo())
+      Tracker.commitFootprint(
+          VD, MutationType::MemberSpecInfo, ID,
+          DeclStateReverter::createMemberSpecializationFootprint(*VD));
+  }
+}
+
+void PTUMutationActions::commitEnum(PTUID ID, const EnumDecl *ED,
+                                    MutationRecord &Rec, bool IsNew) {
+
+  if (Rec.has(MutationType::TypeForDecl)) {
+    // for (const TagDecl *Redecl : ED->redecls()) {
+    //   const Type *Now = Redecl->getTypeForDecl();
+    //   const Type *LastKnown = nullptr;
+    //   if (!IsNew) {
+    //     const auto *Last = Tracker.getFootprints().mostRecent<const Type *>(
+    //         Redecl, MutationType::TypeForDecl);
+    //     LastKnown = Last ? *Last : nullptr;
+    //   }
+    //   if (IsNew || (LastKnown != Now))
+    //     Tracker.commitFootprint(Redecl, MutationType::TypeForDecl, ID,
+    //                             LastKnown);
+    // }
+  }
+
+  if (Rec.has(MutationType::MemberSpecInfo)) {
+    // A scoped member enumeration of a class template -- instantiated with
+    // the enclosing specialization, carrying MSI back to the pattern enum.
+    if (ED->getMemberSpecializationInfo())
+      Tracker.commitFootprint(
+          ED, MutationType::MemberSpecInfo, ID,
+          DeclStateReverter::createMemberSpecializationFootprint(*ED));
+  }
+}
+
+void PTUMutationActions::commitTemplate(PTUID ID,
+                                        const RedeclarableTemplateDecl *RT,
+                                        MutationRecord &Rec, bool IsNew) {
+  // HiddenMutationTracker keeps both flags per declaration, and Cur.Mutations
+  // (Rec, per PTU) already tells us whether this PTU confirmed it, which is
+  // exactly what restoreTemplate needs, just like restoreVar. Handle both
+  // flags the same way: IsNew registers the declaration for a future sweep;
+  // otherwise, this is the confirmation from SweepTracker::sweep()'s
+  // OnConfirmed callback. Once either flag is true, it is final—
+  // Common/CanonInjectedTST won't reset it on their own—so settle it now
+  // instead of waiting for another sweep.
+  const RedeclarableTemplateDecl *RTCanon = RT->getCanonicalDecl();
+  if (Rec.has(MutationType::TemplateCommon)) {
+    if (IsNew)
+      HiddenMutationTracker.track(RTCanon,
+                                  uint32_t(MutationType::TemplateCommon));
+    else
+      HiddenMutationTracker.settle(RTCanon,
+                                   uint32_t(MutationType::TemplateCommon));
+  }
+
+  if (Rec.has(MutationType::CanonInjectedTST)) {
+    if (IsNew)
+      HiddenMutationTracker.track(RTCanon,
+                                  uint32_t(MutationType::CanonInjectedTST));
+    else
+      HiddenMutationTracker.settle(RTCanon,
+                                   uint32_t(MutationType::CanonInjectedTST));
+  }
+}
+
+void PTUMutationActions::commitTypedef(PTUID ID, const TypedefNameDecl *TD,
+                                       MutationRecord &Rec, bool IsNew) {
+  if (Rec.has(MutationType::TypeForDecl)) {
+    if (IsNew)
+      HiddenMutationTracker.track(TD, uint32_t(MutationType::TypeForDecl));
+    else
+      HiddenMutationTracker.settle(TD, uint32_t(MutationType::TypeForDecl));
   }
 }
 
@@ -872,6 +1003,33 @@ void PTUMutationActions::commitDecl(PTUID ID, const Decl *D,
   }
 }
 
+// respect the LIFO so only current inside map not commited can be commited
+// not randon PTUID
+// global map info shouldn't be commited before only added here. not note*
+// time.
+void PTUMutationActions::commit(TranslationUnitDecl *ThisTU) {
+  PTUStateInfo &Cur = Tracker.current();
+  assert(!Cur.Commited);
+  PTUID ID = Cur.ID;
+
+  Cur.verifyMutations(*this);
+  Tracker.getHiddenMutationTracker().sweep(
+      *this, [&](const Decl *D, DeclShape S, uint32_t K) {
+        Cur.noteMutated(D, S, K);
+      });
+
+  for (auto &[D, Rec] : Cur.Mutations)
+    commitDecl(ID, D, Rec, /*IsNew=*/false);
+
+  DeclStateCommitPolicy Proxy(ID, *this);
+  for (const Decl *D : Cur.ImplicitDecls)
+    Proxy.process(D);
+
+  walkDecls(ThisTU, Proxy);
+
+  Cur.Commited = true;
+}
+
 /// -----------------------------------------------------------------------
 //////////////////////// PTUMutationActions::restore //////////////////////
 /// -----------------------------------------------------------------------
@@ -879,36 +1037,25 @@ void PTUMutationActions::commitDecl(PTUID ID, const Decl *D,
 void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
                                       MutationRecord &Rec) {
 
-  // DefinitionArrived and DefinitionDataChanged commit identically -- both
-  // mean "this record's DefinitionData needs a fresh footprint." They stay
-  // distinct kinds because rollback and dependency-edge attribution treat
-  // them differently (arrival is this PTU completing a record that
-  // predates it; a data change may be either).
   if (Rec.has(MutationType::DefinitionInstantiate) ||
       Rec.has(MutationType::DefinitionData)) {
-    // CXXRecordDecl *RD = cast<CXXRecordDecl>(RD);
-    // New: no prior entry exists, so write unconditionally.
-    // Existing: only write a real diff.
-    /// SpecializationDecl can have lazy implicit generated body;
     if (Rec.has(MutationType::DefinitionInstantiate)) {
       DeclStateReverter::revertDefinitionArrival(
           *const_cast<CXXRecordDecl *>(RD));
-    } else {
-      auto *Chain = Tracker.getChainFor(RD);
-      if (Chain) {
-        if (const DefinitionDataFootprint *Restored = Chain->mostRecent())
-          DeclStateReverter::restoreDefinitionDataFootprint(
-              *Restored, *const_cast<CXXRecordDecl *>(RD));
-      }
+    } else if (const auto *Restored =
+                   Tracker.getFootprints().mostRecent<DefinitionDataFootprint>(
+                       RD, MutationType::DefinitionData)) {
+      DeclStateReverter::restoreDefinitionDataFootprint(
+          *Restored, *const_cast<CXXRecordDecl *>(RD));
     }
   }
 
   // TypeForDecl is shared across the whole redeclaration chain -- revert all.
   if (Rec.has(MutationType::TypeForDecl)) {
-    // CXXRecordDecl *RD = cast<CXXRecordDecl>(D);
     // for (const TagDecl *Redecl : RD->redecls()) {
-    //   const Type *LastKnown =
-    //       Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+    //   const auto *Last = Tracker.Footprints.mostRecent<const Type *>(
+    //       Redecl, MutationType::TypeForDecl);
+    //   const Type *LastKnown = Last ? *Last : nullptr;
     //   TODO: need to handle?
     // }
   }
@@ -918,22 +1065,22 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
     // reaching here means a note*() wrapper passed the wrong kind.
     const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD);
     assert(Spec && "SpecializationAdded noted on a non-specialization");
-    if (auto *Chain = Tracker.getChainFor(Spec)) {
-      if (const SpecializationFootprint *Restored = Chain->mostRecent())
-        DeclStateReverter::restoreSpecializationFootprint(
-            *Restored, *const_cast<ClassTemplateSpecializationDecl *>(Spec));
-    }
+    if (const SpecializationFootprint *Restored =
+            Tracker.getFootprints().mostRecent<SpecializationFootprint>(
+                Spec, MutationType::SpecInfo))
+      DeclStateReverter::restoreSpecializationFootprint(
+          *Restored, *const_cast<ClassTemplateSpecializationDecl *>(Spec));
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
     // A nested member of a class template – its MSI kind or
     // point-of-instantiation changed.
     assert(RD->getMemberSpecializationInfo());
-    if (auto *Chain = Tracker.getMemberSpecChainFor(RD)) {
-      if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
-        DeclStateReverter::restoreMemberSpecializationFootprint(
-            *Restored, *const_cast<CXXRecordDecl *>(RD));
-    }
+    if (const MemberSpecializationFootprint *Restored =
+            Tracker.getFootprints().mostRecent<MemberSpecializationFootprint>(
+                RD, MutationType::MemberSpecInfo))
+      DeclStateReverter::restoreMemberSpecializationFootprint(
+          *Restored, *const_cast<CXXRecordDecl *>(RD));
   }
 }
 
@@ -947,9 +1094,9 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
   if (Rec.has(MutationType::ExceptionSpec) ||
       Rec.has(MutationType::DeducedReturnType)) {
     for (const FunctionDecl *Redecl : FD->redecls()) {
-      QualType LastKnown =
-          Tracker.getFunctionTypeMutations().mostRecent(Redecl).value_or(
-              QualType());
+      const auto *Last = Tracker.getFootprints().mostRecent<QualType>(
+          Redecl, MutationType::ExceptionSpec);
+      QualType LastKnown = Last ? *Last : QualType();
       if (LastKnown != Redecl->getType())
         const_cast<FunctionDecl *>(Redecl)->setType(LastKnown);
     }
@@ -963,20 +1110,20 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
     // FunctionTemplateSpecializationInfo.
     assert(FD->getTemplateSpecializationKind() != TSK_Undeclared &&
            "SpecInfoChanged on a function with no specialization info");
-    if (auto *Chain = Tracker.getChainFor(FD)) {
-      if (const FunctionSpecializationFootprint *Restored = Chain->mostRecent())
-        DeclStateReverter::restoreFunctionSpecializationFootprint(
-            *Restored, *const_cast<FunctionDecl *>(FD));
-    }
+    if (const auto *Restored =
+            Tracker.Footprints.mostRecent<FunctionSpecializationFootprint>(
+                FD, MutationType::SpecInfo))
+      DeclStateReverter::restoreFunctionSpecializationFootprint(
+          *Restored, *const_cast<FunctionDecl *>(FD));
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
     assert(FD->getMemberSpecializationInfo());
-    if (auto *Chain = Tracker.getMemberSpecChainFor(FD)) {
-      if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
-        DeclStateReverter::restoreMemberSpecializationFootprint(
-            *Restored, *const_cast<FunctionDecl *>(FD));
-    }
+    if (const auto *Restored =
+            Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+                FD, MutationType::MemberSpecInfo))
+      DeclStateReverter::restoreMemberSpecializationFootprint(
+          *Restored, *const_cast<FunctionDecl *>(FD));
   }
 }
 
@@ -1045,27 +1192,26 @@ void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
   }
 
   if (Rec.has(VarMutation::DefinitionInstantiate)) {
-    /// TODO;
     DeclStateReverter::revertDefinitionArrival(*const_cast<VarDecl *>(VD));
   }
 
   if (Rec.has(VarMutation::SpecInfo)) {
     // Unlike functions, a variable specialization IS a distinct type.
     const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
-    if (auto *Chain = Tracker.getChainFor(Spec)) {
-      if (const VarSpecializationFootprint *Restored = Chain->mostRecent())
-        DeclStateReverter::restoreVarSpecializationFootprint(
-            *Restored, *const_cast<VarTemplateSpecializationDecl *>(Spec));
-    }
+    if (const auto *Restored =
+            Tracker.getFootprints().mostRecent<VarSpecializationFootprint>(
+                Spec, MutationType::SpecInfo))
+      DeclStateReverter::restoreVarSpecializationFootprint(
+          *Restored, *const_cast<VarTemplateSpecializationDecl *>(Spec));
   }
 
   if (Rec.has(VarMutation::MemberSpecInfo)) {
     assert(VD->getMemberSpecializationInfo());
-    if (auto *Chain = Tracker.getMemberSpecChainFor(VD)) {
-      if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
-        DeclStateReverter::restoreMemberSpecializationFootprint(
-            *Restored, *const_cast<VarDecl *>(VD));
-    }
+    if (const auto *Restored =
+            Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+                VD, MutationType::MemberSpecInfo))
+      DeclStateReverter::restoreMemberSpecializationFootprint(
+          *Restored, *const_cast<VarDecl *>(VD));
   }
 }
 
@@ -1077,24 +1223,26 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
   // defined later), so every redecl's cached pointer needs checking.
   if (Rec.has(MutationType::TypeForDecl)) {
     // for (const TagDecl *Redecl : ED->redecls()) {
-    //   const Type *LastKnown =
-    //       Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+    //   const auto *Last = Tracker.Footprints.mostRecent<const Type *>(
+    //       Redecl, MutationType::TypeForDecl);
+    //   const Type *LastKnown = Last ? *Last : nullptr;
     //   TODO: need to handle?
     // }
   }
 
-  if (Rec.has(MutationType::DefinitionInstantiate))
-    DeclStateReverter::revertDefinitionArrival(*const_cast<EnumDecl *>(ED));
+  //   if (Rec.has(MutationType::DefinitionInstantiate))
+  //     DeclStateReverter::revertDefinitionArrival(*const_cast<EnumDecl
+  //     *>(ED));
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
     // A scoped member enumeration of a class template -- instantiated with
     // the enclosing specialization, carrying MSI back to the pattern enum.
     assert(ED->getMemberSpecializationInfo());
-    if (auto *Chain = Tracker.getMemberSpecChainFor(ED)) {
-      if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
-        DeclStateReverter::restoreMemberSpecializationFootprint(
-            *Restored, *const_cast<EnumDecl *>(ED));
-    }
+    if (const auto *Restored =
+            Tracker.getFootprints().mostRecent<MemberSpecializationFootprint>(
+                ED, MutationType::MemberSpecInfo))
+      DeclStateReverter::restoreMemberSpecializationFootprint(
+          *Restored, *const_cast<EnumDecl *>(ED));
   }
 }
 
@@ -1340,4 +1488,17 @@ void PTUMutationRecorder::InstantiationRequested(const ValueDecl *D) {
   Cur.noteMutated(D, S, K);
 }
 
+// Each (Owner, Kind) key written by this PTU has its trailing entries
+// removed in one uniform call. There’s no need for per-node-type
+// release() or pool bookkeeping: FootprintStore’s SmallVector-per-key
+// storage manages the memory normally. Once a key’s history is empty,
+// removeFrom erases the key entirely, just as it always has.
+void IncrementalStateTracker::undoLastEntries() {
+  PTUStateInfo &Cur = current();
+  PTUID ID = Cur.ID;
+
+  for (auto &[Owner, K] : Cur.TouchedFootprints)
+    Footprints.removeFrom(Owner, K, ID);
+}
+
 } // end namespace clang
\ No newline at end of file

>From ff21c1a2ea61f9d80bdea9e0f25763ec31bc2bae Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 27 Sep 2026 17:45:55 +0530
Subject: [PATCH 10/13] Improve: add logic that verify unconfirmed mutations
 from listeners and refactor other part of code

---
 .../include/clang/Interpreter/ErrorRecovery.h | 183 ++----
 clang/lib/Interpreter/ErrRecovery.cpp         | 615 +++++++++---------
 clang/lib/Interpreter/IncrementalParser.cpp   |   2 -
 3 files changed, 369 insertions(+), 431 deletions(-)

diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 2c662e9e53017..b8365efc41968 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -17,6 +17,7 @@
 #include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
+#include "llvm/ADT/BitVector.h"
 #include <variant>
 
 namespace clang {
@@ -265,96 +266,24 @@ class DeclStateReverter {
   }
 
 protected:
-  // These all return by value now, not an ASTContext-arena-allocated
-  // pointer: footprints live inside FootprintStore's own SmallVector, which
-  // owns them with ordinary C++ lifetime (ctor/dtor/copy), so there's
-  // nothing left for arena allocation to buy -- the arena's job was only
-  // ever to make these survive without anyone having to free them, and a
-  // normally-owned value does that for free too.
   static DefinitionDataFootprint
   createDefinitionDataFootprint(const CXXRecordDecl &RD);
 
-  static bool compareDefinitionDataFootprint(const DefinitionDataFootprint &FP,
-                                             const CXXRecordDecl &RD);
-
   static void restoreDefinitionDataFootprint(const DefinitionDataFootprint &FP,
                                              CXXRecordDecl &RD);
 
-  static SpecializationFootprint
-  createSpecializationFootprint(const ClassTemplateSpecializationDecl &Spec) {
-    SpecializationFootprint FP;
-    FP.update(Spec);
-    return FP;
-  }
-  static bool
-  compareSpecializationFootprint(const SpecializationFootprint &FP,
-                                 const ClassTemplateSpecializationDecl &Spec) {
-    SpecializationFootprint Live;
-    Live.update(Spec);
-    return FP == Live;
-  }
-  static void
-  restoreSpecializationFootprint(const SpecializationFootprint &FP,
-                                 ClassTemplateSpecializationDecl &Spec) {
-    FP.restore(Spec);
-  }
-
-  static VarSpecializationFootprint
-  createVarSpecializationFootprint(const VarTemplateSpecializationDecl &Spec) {
-    VarSpecializationFootprint FP;
-    FP.update(Spec);
-    return FP;
-  }
-  static bool
-  compareVarSpecializationFootprint(const VarSpecializationFootprint &FP,
-                                    const VarTemplateSpecializationDecl &Spec) {
-    VarSpecializationFootprint Live;
-    Live.update(Spec);
-    return FP == Live;
-  }
-  static void
-  restoreVarSpecializationFootprint(const VarSpecializationFootprint &FP,
-                                    VarTemplateSpecializationDecl &Spec) {
-    FP.restore(Spec);
-  }
-
-  static FunctionSpecializationFootprint
-  createFunctionSpecializationFootprint(const FunctionDecl &FD) {
-    FunctionSpecializationFootprint FP;
-    FP.update(FD);
-    return FP;
-  }
-  static bool compareFunctionSpecializationFootprint(
-      const FunctionSpecializationFootprint &FP, const FunctionDecl &FD) {
-    FunctionSpecializationFootprint Live;
-    Live.update(FD);
-    return FP == Live;
-  }
-  static void restoreFunctionSpecializationFootprint(
-      const FunctionSpecializationFootprint &FP, FunctionDecl &FD) {
-    FP.restore(FD);
-  }
-
-  template <typename OwnerT>
-  static MemberSpecializationFootprint
-  createMemberSpecializationFootprint(const OwnerT &D) {
-    MemberSpecializationFootprint FP;
-    FP.update(D);
+  // Generic across every other footprint type -- SpecializationFootprint,
+  // VarSpecializationFootprint, FunctionSpecializationFootprint,
+  // MemberSpecializationFootprint.
+  template <typename FootprintT, typename OwnerT>
+  static FootprintT createFootprint(const OwnerT &Owner) {
+    FootprintT FP;
+    FP.update(Owner);
     return FP;
   }
-  template <typename OwnerT>
-  static bool
-  compareMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
-                                       const OwnerT &D) {
-    MemberSpecializationFootprint Live;
-    Live.update(D);
-    return FP == Live;
-  }
-  template <typename OwnerT>
-  static void
-  restoreMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
-                                       OwnerT &D) {
-    FP.restore(D);
+  template <typename FootprintT, typename OwnerT>
+  static void restoreFootprint(const FootprintT &FP, OwnerT &Owner) {
+    FP.restore(Owner);
   }
 
   static void restoreDefinitionAndRevertDC(CXXRecordDecl &RD);
@@ -389,10 +318,6 @@ class DeclStateReverter {
   // specializations added by a given PTU form a trailing run at the back of
   // the FoldingSetVector. Popping entries while the back entry belongs to
   // this PTU is therefore sufficient.
-  //
-  // FoldingSetVector also has no arbitrary-position erase – only
-  // pop_back()/clear() – which is another reason to walk from the back
-  // rather than filter the entries in place.
   static void removeSpecializations(PTUCheckpointLedger &Ledger,
                                     const RedeclarableTemplateDecl *TD,
                                     PTUID ID);
@@ -429,13 +354,12 @@ class DeclStateReverter {
 
   static void detachCommonBase(const RedeclarableTemplateDecl *RT);
 
-  /// Remove D from its semantic context's lookup map, reinstating the
-  /// previous declaration if D had replaced one in-place (which is what
-  /// StoredDeclsList::HandleRedeclaration does on a redeclaration --
-  /// erasing the slot outright would lose the older decl entirely; that
-  /// is the ReopenNs failure).
   void detachFromDCLookup(Decl *D, PTUID ID);
 
+  void detachFromExternCLookup(Decl *D, DeclContext *ExternCCtx, PTUID ID);
+
+  static void removeFromIdResolver(Sema &S, NamedDecl *D);
+
   // Walk D's redecl chain looking for the newest decl that predates this
   // PTU. Returns nullptr if the entire chain was created this PTU.
   template <typename DeclT> DeclT *findSurvivor(DeclT *D, PTUID ID) const;
@@ -453,18 +377,16 @@ class DeclStateReverter {
 
 private:
   NamedDecl *tryDetachRedeclChain(Decl *D, PTUID ID);
+
+  void removeFromLookupMap(NamedDecl *ND, DeclContext *DC, PTUID ID);
 };
 
 /// Maps addresses to the PTU that allocated them, by keeping the slab
 /// checkpoint taken before each PTU began.
-///
-/// PTU IDs are the vector index, which is what makes the lookup a binary
-/// search: checkpoints are monotonically increasing, so "is this address
-/// after checkpoint N" is true for all N up to the allocating PTU and
-/// false after.
 class PTUCheckpointLedger {
   const ASTContext &Ctx;
   llvm::SmallVector<llvm::SlabCheckPoint, 16> CheckpointBeforePTU;
+  llvm::BitVector Dead;
 
   bool isAfter(const void *Ptr, PTUID ID) const {
     return Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
@@ -478,11 +400,14 @@ class PTUCheckpointLedger {
     assert(ID == CheckpointBeforePTU.size() &&
            "PTUs must be recorded in order");
     CheckpointBeforePTU.push_back(CP);
+    Dead.push_back(false);
   }
 
   /// Was \p Ptr allocated during PTU \p ID or later?
   bool isFromThisPTU(const void *Ptr, PTUID ID) const {
     assert(ID < CheckpointBeforePTU.size());
+    assert(!Dead[ID] &&
+           "querying provenance against a PTU that no longer exists");
     return isAfter(Ptr, ID);
   }
 
@@ -500,13 +425,16 @@ class PTUCheckpointLedger {
         Hi = Mid;
       }
     }
+    assert((!Result || !Dead[*Result]) &&
+           "Ptr belongs to a PTU that was already rolled back");
     return Result;
   }
 
-  /// Drop checkpoints from \p ID onward, after rolling those PTUs back.
-  void undoFrom(PTUID ID) {
-    if (ID < CheckpointBeforePTU.size())
-      CheckpointBeforePTU.resize(ID);
+  /// FIXME: Pop back CheckpointBeforePTU once memory restoration is supported.
+  /// For now, there is no memory restoration, so mark it dead.
+  void markDead(PTUID ID) {
+    assert(ID < Dead.size());
+    Dead[ID] = true;
   }
 };
 
@@ -569,8 +497,7 @@ class FootprintStore {
       Store;
 
 public:
-  // Same dedup StateAwareChain::commit() always had: re-committing the
-  // same value for the same owner+kind is a no-op, not a new history entry.
+  // commit decl state owner+mutation_kind -> Footprint.
   template <typename T>
   void commit(const Decl *Owner, MutationType K, PTUID ID, const T &Fresh) {
     auto &History = Store[{Owner, K}];
@@ -580,9 +507,7 @@ class FootprintStore {
   }
 
   // nullptr means either "no history at all" or "history exists but the
-  // most recent entry isn't a T" -- the latter never legitimately happens
-  // for a given (Owner, K) pair, since K determines which alternative was
-  // ever committed there.
+  // most recent entry isn't a T".
   template <typename T>
   const T *mostRecent(const Decl *Owner, MutationType K) const {
     auto It = Store.find({Owner, K});
@@ -613,15 +538,21 @@ struct PTUStateInfo {
 
   llvm::SmallPtrSet<const Decl *, 4> ImplicitDecls;
 
+  /// Set exactly when AddedCXXImplicitMember fires this PTU -- narrower
+  /// than ImplicitDecls above, which is a shared superset populated by
+  /// many unrelated listener paths too (template instantiation, etc.).
+  /// This is the one listener whose firing can leave a stale
+  /// Sema::SpecialMemberCache entry behind, so it's what
+  /// purgeStaleSpecialMemberCache actually needs to gate on -- gating on
+  /// ImplicitDecls instead would trigger that scan on almost every PTU,
+  /// defeating the point of the gate.
+  bool HadImplicitCXXMember = false;
+
   /// here touched info mean other this belongs to other PTUs;
   llvm::SmallPtrSet<const DeclContext *, 4> TouchedDC;
 
   /// Every (Owner, Kind) key this PTU's own commit() wrote a FootprintStore
-  /// entry for -- replaces the old TouchedFootprintDecls/
-  /// TouchedFunctionTypeOwners/TouchedTagdeclOwners trio (one set per
-  /// container that used to exist) with one list, since there's now only
-  /// one container. Duplicates are harmless: FootprintStore::removeFrom is
-  /// idempotent.
+  /// entry for.
   llvm::SmallVector<std::pair<const Decl *, MutationType>, 8> TouchedFootprints;
 
   explicit PTUStateInfo(PTUID ID) : ID(ID) {}
@@ -738,8 +669,7 @@ class IncrementalStateTracker {
   // Writes a new value and records that this PTU touched (Owner, K), so
   // undoLastEntries() knows what to remove if the PTU is rolled back
   // without committing. mostRecent() doesn't need any bookkeeping, so it
-  // reads directly from Footprints via Tracker.Footprints.mostRecent<T>(...)
-  // instead of having its own wrapper.
+  // reads directly from Footprints via Tracker.Footprints.mostRecent<T>(...).
   template <typename T>
   void commitFootprint(const Decl *Owner, MutationType K, PTUID ID,
                        const T &Fresh) {
@@ -773,6 +703,7 @@ class IncrementalStateTracker {
   SweepTracker &getHiddenMutationTracker() { return HiddenMutationTracker; }
   PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
   Sema &getSema() const { return SemaRef; }
+  ASTContext &getASTContext() const { return Ctx; }
 
   void undoLastEntries();
 
@@ -780,8 +711,7 @@ class IncrementalStateTracker {
   void popCurrentPTU() {
     PTUID ID = currentID();
     PTUStack.pop_back();
-    PTUSlabCheckpoints.undoFrom(ID);
-    NextID = ID;
+    PTUSlabCheckpoints.markDead(ID);
   }
 };
 
@@ -802,7 +732,11 @@ class PTUMutationActions {
 
   uint32_t DeclNeedingTracking(DeclShape S, const Decl *D);
 
-  uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
+  uint32_t confirmMutation(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
+
+  void registerLiveVerification(const Decl *D, uint32_t Kinds);
+
+  void settleIfClosed(const Decl *D, DeclShape S, uint32_t Confirmed);
 
 private:
   void commitMembers(PTUID ID, const DeclContext *Members);
@@ -837,12 +771,23 @@ class PTUMutationActions {
   void restoreDecl(PTUID ID, const Decl *D, MutationRecord &Rec);
   void commitDecl(PTUID ID, const Decl *D, MutationRecord &Rec,
                   bool IsNew = false);
-  // respect the LIFO so only current inside map not commited can be commited
-  // not randon PTUID
-  // global map info shouldn't be commited before only added here. not note*
-  // time.
+  // Actual commit action: updates IncrementalStateTracker tracking info for
+  // decls from the previous PTU after a successful PTU. And add tracking info
+  // for newly created Decls
+  //
   void commit(TranslationUnitDecl *MostRecentTU);
+
+  // Restore action: restores decls from the previous PTU using
+  // IncrementalStateTracker tracking info and unlinks decls created by this
+  // PTU.
   void restore(TranslationUnitDecl *MostRecentTU);
+
+private:
+  // Sema::SpecialMemberCache, per (RD,
+  // kind+qualifiers), the CXXMethodDecl* a prior LookupSpecialMember()
+  // call resolved to, with no re-validation on a cache hit. remove every
+  // entry whose cached method belongs to the PTU being rolled back.
+  void restoreSpecialMemberCache(PTUID ID);
 };
 
 class PTUMutationRecorder : public ASTMutationListener {
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index 5c61e0165507f..509d0d0a40668 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -21,13 +21,13 @@
 
 namespace clang {
 
-#define PTU_SHAPES                                                             \
-  PTU_SHAPE(Class, CXXRecordDecl)                                              \
-  PTU_SHAPE(Function, FunctionDecl)                                            \
-  PTU_SHAPE(Var, VarDecl)                                                      \
-  PTU_SHAPE(Enum, EnumDecl)                                                    \
-  PTU_SHAPE(Template, RedeclarableTemplateDecl)                                \
-  PTU_SHAPE(Typedef, TypedefNameDecl)
+#define DECL_SHAPES                                                            \
+  DECL_SHAPE(Class, CXXRecordDecl)                                             \
+  DECL_SHAPE(Function, FunctionDecl)                                           \
+  DECL_SHAPE(Var, VarDecl)                                                     \
+  DECL_SHAPE(Enum, EnumDecl)                                                   \
+  DECL_SHAPE(Template, RedeclarableTemplateDecl)                               \
+  DECL_SHAPE(Typedef, TypedefNameDecl)
 
 /// -----------------------------------------------------------------------
 //////////////////////// PTUMutationActions::Helpers ///////////////////////
@@ -42,15 +42,15 @@ DeclStateReverter::createDefinitionDataFootprint(const CXXRecordDecl &RD) {
   return FP;
 }
 
-bool DeclStateReverter::compareDefinitionDataFootprint(
-    const DefinitionDataFootprint &FP, const CXXRecordDecl &RD) {
-  const auto &Live = RD.data();
-#define FIELD(Name, Width, Merge)                                              \
-  if (FP.Name != Live.Name)                                                    \
-    return false;
-#include "clang/AST/CXXRecordDeclDefinitionBits.def"
-  return true;
-}
+// bool DeclStateReverter::compareDefinitionDataFootprint(
+//     const DefinitionDataFootprint &FP, const CXXRecordDecl &RD) {
+//   const auto &Live = RD.data();
+// #define FIELD(Name, Width, Merge) \
+//   if (FP.Name != Live.Name) \
+//     return false;
+// #include "clang/AST/CXXRecordDeclDefinitionBits.def"
+//   return true;
+// }
 
 void DeclStateReverter::restoreDefinitionDataFootprint(
     const DefinitionDataFootprint &FP, CXXRecordDecl &RD) {
@@ -59,23 +59,15 @@ void DeclStateReverter::restoreDefinitionDataFootprint(
 #include "clang/AST/CXXRecordDeclDefinitionBits.def"
 }
 
-// NEEDS: friend class DeclStateReverter; in CXXRecordDecl (DefinitionData
-// is PRIVATE there -- already granted) and nothing further for the
-// DeclContext/TagDecl parts below, which are routed through the
-// protected accessors above instead.
 void DeclStateReverter::restoreDefinitionAndRevertDC(CXXRecordDecl &RD) {
   // 1. DefinitionData pointer -- back to null.
   RD.DefinitionData = nullptr;
 
-  // 2. DeclContext member list -- back to empty. RD's own FirstDecl/
-  // LastDecl were populated by InstantiateClassImpl substituting RD's
-  // members for the first time (SemaTemplateInstantiate.cpp:3658-3729,
-  // and for the static_assert/friend-decl case specifically,
-  // Sema::BuildStaticAssertDeclaration's CurContext->addDecl(), which
-  // never notifies since neither is a NamedDecl) -- an incomplete,
-  // never-defined class's DeclContext has neither, so this is RD's
-  // true prior state, not a guess.
+  // 2. DeclContext member list -- back to empty.
   clearDeclContextChain(RD);
+  DeclContext *Primary = RD.getDeclContext()->getPrimaryContext();
+  if (StoredDeclsMap *Map = Primary->getLookupPtr())
+    Map->clear();
 
   // 3. TagDecl completion bits -- back to "never started". Flipped by
   // startDefinition()/completeDefinition().
@@ -128,7 +120,6 @@ void DeclStateReverter::removeSpecializations(
 }
 
 void DeclStateReverter::detachDefData(const Decl *D) {
-  // TODO: handle Template's own separate redecl chain the same way.
   const auto *RD = dyn_cast<CXXRecordDecl>(D);
   if (!RD)
     return;
@@ -138,15 +129,35 @@ void DeclStateReverter::detachDefData(const Decl *D) {
 }
 
 void DeclStateReverter::detachCommonBase(const RedeclarableTemplateDecl *RT) {
-  // Common is shared across the WHOLE redecl chain once path-compression
-  // (RedeclarableTemplateDecl::getCommonPtr()'s backward walk-then-
-  // backfill) has run -- resetting only the one decl this PTU created
-  // would leave some other redecl holding a stale, dangling copy of the
-  // same pointer. Walk every redecl, not just a backward chain from RT.
   for (const auto *R : RT->redecls())
     clearCommonPtr(*R);
 }
 
+void DeclStateReverter::removeFromLookupMap(NamedDecl *ND, DeclContext *DC,
+                                            PTUID ID) {}
+
+void DeclStateReverter::detachFromExternCLookup(Decl *D,
+                                                DeclContext *ExternCCtx,
+                                                PTUID ID) {
+  auto *ND = dyn_cast<NamedDecl>(D);
+  if (!ND)
+    return;
+
+  bool IsExternC = false;
+  if (const auto *FD = dyn_cast<FunctionDecl>(ND))
+    IsExternC = FD->isExternC();
+  else if (const auto *VD = dyn_cast<VarDecl>(ND))
+    IsExternC = VD->isExternC();
+
+  if (IsExternC)
+    removeFromLookupMap(ND, ExternCCtx, ID);
+}
+
+void DeclStateReverter::removeFromIdResolver(Sema &S, NamedDecl *D) {
+  if (D->getDeclName().getFETokenInfo())
+    S.IdResolver.RemoveDecl(D);
+}
+
 /// Remove D from every lookup map it became visible in, reinstating the
 /// previous declaration where D had replaced one in-place (which is what
 /// StoredDeclsList::HandleRedeclaration does on a redeclaration --
@@ -422,75 +433,48 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
   return 0;
 }
 
-/// Given a Decl known to be DeclShape S with FlaggedKinds set, return the
-/// subset of kinds that are actually confirmed.
-///
-/// Everything reaching this function through verifyMutations() belongs to
-/// a decl from an earlier PTU. Same-PTU mutations are ignored by the
-/// reactive listeners, so these mutations were already found during an
-/// earlier commit().
-///
-/// Some mutation kinds are fully confirmed by their listener, so there is
-/// no need to walk the chain for them:
-/// - DefinitionInstantiate, SpecializationAdded, DefinitionData
-/// - ExceptionSpec, DeducedReturnType
-///
-/// For these, the listener itself is the source of truth for the change.
-/// In particular, don’t use the FunctionTypeMutations chain for
-/// ExceptionSpec/DeducedReturnType here. That chain is populated later by
-/// commitFunctionFamily(), so checking it here would miss the first
-/// resolution.
+template <typename T>
+static bool differsFromLast(IncrementalStateTracker &Tracker, const Decl *Owner,
+                            MutationType K, const T &LiveValue) {
+  const auto *Last = Tracker.getFootprints().mostRecent<T>(Owner, K);
+  assert(Last && "baseline missing at verify time");
+  return !Last || *Last != LiveValue;
+}
+
+template <typename FootprintT, typename OwnerT>
+static FootprintT Footprint(const OwnerT &Owner) {
+  FootprintT FP;
+  FP.update(Owner);
+  return FP;
+}
+
+/// The single place to check whether a kind actually changed.
 ///
-/// The remaining kinds don’t have a reliable listener, so they need to be
-/// checked against the stored state:
-/// - SpecInfo, MemberSpecInfo
-/// - Template Common/CanonInjectedTST
-/// - Var EvaluatedValue
-/// - Typedef TypeForDecl
+/// It handles both listener-less kinds and SpecInfo/MemberSpecInfo. The
+/// latter can be checked from both the sweep and the listener re-check.
 ///
-/// For footprint-based checks, the baseline should already be there since
-/// it is seeded when the decl is first discovered. If it’s missing here,
-/// assert in debug builds, but still confirm the mutation. commitDecl()
-/// will create the missing chain while committing, which fixes up the
-/// baseline for future checks.
-uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
-                                               uint32_t FlaggedKinds) {
-
+/// Keeping everything in one function makes sure SpecInfo/MemberSpecInfo
+/// changes are caught even when the listener never fires.
+uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
+                                             uint32_t FlaggedKinds) {
   uint32_t Verified = 0;
-  Verified |= FlaggedKinds & uint32_t(MutationType::DefinitionInstantiate);
   switch (S) {
   case DeclShape::Class: {
     const auto *RD = cast<CXXRecordDecl>(D);
-    // AddedCXXImplicitMember firing is itself the confirmation -- see
-    // this function's own top comment for why no chain lookup belongs
-    // here.
-    Verified |= FlaggedKinds & uint32_t(MutationType::DefinitionData);
     if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
-      //   const Type *Now = DeclStateReverter::getRawTypeForDecl(RD);
-      //   const auto *Last =
-      //       Tracker.Footprints.mostRecent<const Type *>(RD,
-      //       MutationType::TypeForDecl);
-      //   assert(Last && "TypeForDecl baseline missing at verify time");
-      //   if (!Last || *Last != Now)
-      //     Verified |= uint32_t(MutationType::TypeForDecl);
+      // No listener at all for this mutation.
+      // const Type *Now = DeclStateReverter::getRawTypeForDecl(RD);
+      // if (differsFromLast(Tracker, RD, MutationType::TypeForDecl, Now))
+      //   Verified |= uint32_t(MutationType::TypeForDecl);
     }
     if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
-      if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
-        const auto *Last =
-            Tracker.Footprints.mostRecent<SpecializationFootprint>(
-                Spec, MutationType::SpecInfo);
-        assert(Last && "SpecInfo baseline missing at verify time");
-        if (!Last ||
-            !DeclStateReverter::compareSpecializationFootprint(*Last, *Spec))
+      if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+        if (differsFromLast(Tracker, Spec, MutationType::SpecInfo,
+                            Footprint<SpecializationFootprint>(*Spec)))
           Verified |= uint32_t(MutationType::SpecInfo);
-      }
     } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
-      const auto *Last =
-          Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
-              RD, MutationType::MemberSpecInfo);
-      assert(Last && "MemberSpecInfo baseline missing at verify time");
-      if (!Last ||
-          !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *RD))
+      if (differsFromLast(Tracker, RD, MutationType::MemberSpecInfo,
+                          Footprint<MemberSpecializationFootprint>(*RD)))
         Verified |= uint32_t(MutationType::MemberSpecInfo);
     }
     break;
@@ -498,93 +482,61 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
   case DeclShape::Function: {
     const auto *FD = cast<FunctionDecl>(D);
     if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
-      const auto *Last =
-          Tracker.Footprints.mostRecent<FunctionSpecializationFootprint>(
-              FD, MutationType::SpecInfo);
-      assert(Last && "SpecInfo baseline missing at verify time");
-      if (!Last || !DeclStateReverter::compareFunctionSpecializationFootprint(
-                       *Last, *FD))
+      if (differsFromLast(Tracker, FD, MutationType::SpecInfo,
+                          Footprint<FunctionSpecializationFootprint>(*FD)))
         Verified |= uint32_t(MutationType::SpecInfo);
     } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
-      const auto *Last =
-          Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
-              FD, MutationType::MemberSpecInfo);
-      assert(Last && "MemberSpecInfo baseline missing at verify time");
-      if (!Last ||
-          !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *FD))
+      if (differsFromLast(Tracker, FD, MutationType::MemberSpecInfo,
+                          Footprint<MemberSpecializationFootprint>(*FD)))
         Verified |= uint32_t(MutationType::MemberSpecInfo);
     }
-    // ResolvedExceptionSpec/DeducedReturnType firing is itself the
-    // confirmation.
-    Verified |= FlaggedKinds & (uint32_t(MutationType::ExceptionSpec) |
-                                uint32_t(MutationType::DeducedReturnType));
-    break;
+    break; // Function carries no listener-less kind at all.
   }
   case DeclShape::Var: {
     const auto *VD = cast<VarDecl>(D);
-    if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
-      if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
-        const auto *Last =
-            Tracker.Footprints.mostRecent<VarSpecializationFootprint>(
-                Spec, MutationType::SpecInfo);
-        assert(Last && "SpecInfo baseline missing at verify time");
-        if (!Last ||
-            !DeclStateReverter::compareVarSpecializationFootprint(*Last, *Spec))
-          Verified |= uint32_t(MutationType::SpecInfo);
-      }
-    } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
-      const auto *Last =
-          Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
-              VD, MutationType::MemberSpecInfo);
-      assert(Last && "MemberSpecInfo baseline missing at verify time");
-      if (!Last ||
-          !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *VD))
-        Verified |= uint32_t(MutationType::MemberSpecInfo);
-    }
     if (FlaggedKinds & uint32_t(MutationType::EvaluatedValue) &&
         Tracker.getHiddenMutationTracker().isTrackedFor(
             VD, uint32_t(MutationType::EvaluatedValue))) {
-      // No chain to compare against -- write-once field, so presence of
-      // WasEvaluated=true right now IS the confirmation.
+      // No chain to compare against -- write-once field.
       const EvaluatedStmt *Eval = VD->getEvaluatedStmt();
       if (Eval && Eval->WasEvaluated)
         Verified |= uint32_t(MutationType::EvaluatedValue);
     }
+    if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
+      if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD))
+        if (differsFromLast(Tracker, Spec, MutationType::SpecInfo,
+                            Footprint<VarSpecializationFootprint>(*Spec)))
+          Verified |= uint32_t(MutationType::SpecInfo);
+    } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
+      if (differsFromLast(Tracker, VD, MutationType::MemberSpecInfo,
+                          Footprint<MemberSpecializationFootprint>(*VD)))
+        Verified |= uint32_t(MutationType::MemberSpecInfo);
+    }
     break;
   }
   case DeclShape::Enum: {
     const auto *ED = cast<EnumDecl>(D);
-    // if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
-    //   const Type *Now = DeclStateReverter::getRawTypeForDecl(ED);
-    //   const auto *Last =
-    //       Tracker.Footprints.mostRecent<const Type *>(ED,
-    //       MutationType::TypeForDecl);
-    //   assert(Last && "TypeForDecl baseline missing at verify time");
-    //   if (!Last || *Last != Now)
-    //     Verified |= uint32_t(MutationType::TypeForDecl);
-    // }
+    if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
+      // const Type *Now = DeclStateReverter::getRawTypeForDecl(ED);
+      // if (differsFromLast(Tracker, ED, MutationType::TypeForDecl, Now))
+      //   Verified |= uint32_t(MutationType::TypeForDecl);
+    }
     if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
-      const auto *Last =
-          Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
-              ED, MutationType::MemberSpecInfo);
-      assert(Last && "MemberSpecInfo baseline missing at verify time");
-      if (!Last ||
-          !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *ED))
+      if (differsFromLast(Tracker, ED, MutationType::MemberSpecInfo,
+                          Footprint<MemberSpecializationFootprint>(*ED)))
         Verified |= uint32_t(MutationType::MemberSpecInfo);
     }
     break;
   }
   case DeclShape::Template: {
-    // SpecializationAdded is known directly from
-    // AddedCXXTemplateSpecialization firing, so
-    // always confirmed when flagged.
-    Verified |= FlaggedKinds & uint32_t(MutationType::SpecializationAdded);
+    // Use the canonical key. commitTemplate() tracks and settles CommonBase
+    // modifications, and CommonPtr is shared across the redeclaration chain.
+    // Any modification to CommonPtr is reflected in the canonical declaration.
     const auto *RT = cast<RedeclarableTemplateDecl>(D)->getCanonicalDecl();
     if (FlaggedKinds & uint32_t(MutationType::TemplateCommon) &&
         Tracker.getHiddenMutationTracker().isTrackedFor(
             RT, uint32_t(MutationType::TemplateCommon))) {
-      // No chain to compare against -- write-once, so Common's own
-      // existence right now IS the confirmation.
+      // No chain to compare against -- write-once.
       if (DeclStateReverter::isCommonPtrValid(*RT))
         Verified |= uint32_t(MutationType::TemplateCommon);
     }
@@ -595,40 +547,71 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
         if (DeclStateReverter::isTemplateCanonInjectedTSTValid(CTD))
           Verified |= uint32_t(MutationType::CanonInjectedTST);
     }
-  } break;
+    break;
+  }
   case DeclShape::Typedef: {
     const auto *TD = cast<TypedefNameDecl>(D);
     if (FlaggedKinds & uint32_t(MutationType::TypeForDecl) &&
         Tracker.getHiddenMutationTracker().isTrackedFor(
             TD, uint32_t(MutationType::TypeForDecl))) {
-      // No chain to compare against -- write-once field (unlike
-      // TagDecl's TypeForDecl, this one is guarded by ASTContext's own
-      // `if (Decl->TypeForDecl) return`, never overwritten a second
-      // time), so non-null right now IS the confirmation.
-      //   if (DeclStateReverter::getRawTypeForDecl(TD))
-      //     Verified |= uint32_t(MutationType::TypeForDecl);
+      // No chain to compare against -- write-once field.
+      // if (DeclStateReverter::getRawTypeForDecl(TD))
+      //   Verified |= uint32_t(MutationType::TypeForDecl);
     }
-  } break;
+    break;
+  }
   case DeclShape::None:
-    break; // never flagged in the first place.
+    break;
   }
   return Verified;
 }
 
 template <typename OnConfirmedFn>
 void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
-  //   llvm::SmallVector<std::pair<const Decl *, uint32_t>, 8> Confirmed;
   for (auto &Entry : Active) {
     const Decl *D = Entry.getFirst();
     uint32_t StillOpen = Entry.getSecond();
     DeclShape S = classifyShape(D);
-    if (uint32_t Newly = Act.verifyMutationFor(D, S, StillOpen)) {
+    if (uint32_t Newly = Act.confirmMutation(D, S, StillOpen)) {
       OnConfirmed(D, S, Newly);
-      //   Confirmed.emplace_back(D, Newly);
     }
   }
-  //   for (auto &[D, Newly] : Confirmed)
-  //     settle(D, Newly);
+}
+
+// These kinds have listeners, but the listener alone can’t reliably confirm
+// a real change. So, compare the current value with the last known value,
+// just like we do for kinds without listeners.
+//
+// These kinds are also checked by the sweep. This is needed because the
+// listener can report a change that didn’t happen, or fail to fire when a
+// real change did happen. The sweep catches the second case.
+static constexpr uint32_t KindsNeedingVerification =
+    uint32_t(MutationType::SpecInfo) | uint32_t(MutationType::MemberSpecInfo);
+
+/// Start tracking D for the mutation kinds that need live verification.
+/// Called when a new mutation is committed or an undone mutation is restored.
+/// The callers have already established which kinds need tracking, so no
+/// additional DeclNeedingTracking check is needed.
+void PTUMutationActions::registerLiveVerification(const Decl *D,
+                                                  uint32_t Kinds) {
+  if (uint32_t Live = Kinds & KindsNeedingVerification)
+    HiddenMutationTracker.track(D, Live);
+}
+
+/// Stop tracking Decl D when its mutation no longer needs live verification.
+/// Called from verifyMutations() and the !IsNew SpecInfo/MemberSpecInfo
+/// paths in commitDecl. Re-check DeclNeedingTracking to match the same
+/// "is this still open?" condition used when D is first discovered:
+/// keep tracking if a later PTU can still update it; otherwise settle it.
+void PTUMutationActions::settleIfClosed(const Decl *D, DeclShape S,
+                                        uint32_t Confirmed) {
+  uint32_t Live = Confirmed & KindsNeedingVerification;
+  if (!Live)
+    return;
+  uint32_t StillOpen = DeclNeedingTracking(S, D) & Live;
+  uint32_t Closed = Live & ~StillOpen;
+  if (Closed && HiddenMutationTracker.isTrackedFor(D, Closed))
+    HiddenMutationTracker.settle(D, Closed);
 }
 
 /// Verify recorded mutations and remove any spurious ones. If none of a
@@ -636,10 +619,15 @@ void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
 /// restore() does not pop chain entries that were never written.
 void PTUStateInfo::verifyMutations(PTUMutationActions &Actions) {
   Mutations.remove_if([&](auto &Entry) -> bool {
-    uint32_t Confirmed = Actions.verifyMutationFor(Entry.first, Entry.second.S,
-                                                   Entry.second.MutationType);
-    Entry.second.MutationType = Confirmed;
-    return Confirmed == 0;
+    const Decl *D = Entry.first;
+    MutationRecord &Rec = Entry.second;
+    uint32_t Flagged = Rec.MutationType & KindsNeedingVerification;
+    if (!Flagged)
+      return false; // nothing here needs live verification.
+    uint32_t Confirmed = Actions.confirmMutation(D, Rec.S, Flagged);
+    Actions.settleIfClosed(D, Rec.S, Confirmed);
+    Rec.MutationType = (Rec.MutationType & ~Flagged) | Confirmed;
+    return Rec.MutationType == 0;
   });
 }
 
@@ -702,61 +690,55 @@ class DeclStateUnlinkPolicy {
   }
 
   void process(const Decl *D) {
+    Reverter.detachFromExternCLookup(
+        const_cast<Decl *>(D), Tracker.getASTContext().getExternCContextDecl(),
+        ID);
+    if (auto *ND = dyn_cast<NamedDecl>(const_cast<Decl *>(D)))
+      DeclStateReverter::removeFromIdResolver(Tracker.getSema(), ND);
+
     const Decl *Canon = D->getCanonicalDecl();
 
-    /// Keyed on the canonical decl: every redeclaration shares one
-    /// RedeclLink, so truncating twice would step past the intended survivor.
+    // Whether Candidate's own DefinitionData belongs to this PTU and needs
+    // detaching.
+    auto needsDefDataDetach = [&](const Decl *Candidate) {
+      const auto *RD = dyn_cast<CXXRecordDecl>(Candidate);
+      const CXXRecordDecl *Def = RD ? RD->getDefinition() : nullptr;
+      return Def && Tracker.isFromThisPTU(Def, ID);
+    };
+
+    /// Keyed by the canonical declaration: all redeclarations share a single
+    /// RedeclLink, so truncating more than once would advance past the intended
+    /// surviving declaration.
     bool DetachFromRedecl = !RepairedChains.count(Canon) &&
                             D->getPreviousDecl() &&
                             !Tracker.isFromThisPTU(Canon, ID);
 
+    if (needsDefDataDetach(D))
+      Reverter.detachDefData(D);
+
     // Keyed on the primary context, a different granularity: a reopened
     // namespace's redeclarations all resolve to the same primary.
-    const DeclContext *DC = D->getDeclContext()->getPrimaryContext();
-    bool DetachFromDC = TouchedDC.contains(DC);
-
-    bool DetachFromLexicalChain =
-        D->isImplicit() &&
-        !RepairedLexicalContexts.count(D->getLexicalDeclContext());
-
-    bool DetachDefData = false;
-    bool HasTemplated = false;
-    bool TemplatedDetachDefData = false;
-    if (const auto *RT = dyn_cast<RedeclarableTemplateDecl>(D)) {
-      const NamedDecl *T = RT->getTemplatedDecl();
-      HasTemplated = true;
-      if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(T)) {
-        if (const CXXRecordDecl *Def = RD->getDefinition();
-            Def && Tracker.isFromThisPTU(Def, ID))
-          TemplatedDetachDefData = true;
-      }
-    } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
-      if (const CXXRecordDecl *Def = RD->getDefinition();
-          Def && Tracker.isFromThisPTU(Def, ID))
-        DetachDefData = true;
-    }
-
-    if (DetachDefData)
-      Reverter.detachDefData(D);
-    if (DetachFromDC)
+    if (const DeclContext *DC = D->getDeclContext()->getPrimaryContext();
+        TouchedDC.contains(DC))
       Reverter.detachFromDCLookup(const_cast<Decl *>(D), ID);
+
     if (DetachFromRedecl) {
       RepairedChains.insert(Canon);
       Reverter.detachFromRedeclChain(D, ID);
     }
 
-    if (HasTemplated) {
-      const Decl *T = cast<RedeclarableTemplateDecl>(D)->getTemplatedDecl();
-      if (TemplatedDetachDefData)
-        Reverter.detachDefData(T);
+    if (const auto *RT = dyn_cast<RedeclarableTemplateDecl>(D)) {
+      const NamedDecl *Templated = RT->getTemplatedDecl();
+      if (needsDefDataDetach(Templated))
+        Reverter.detachDefData(Templated);
       if (DetachFromRedecl)
-        Reverter.detachFromRedeclChain(T, ID);
+        Reverter.detachFromRedeclChain(Templated, ID);
     }
 
-    if (DetachFromLexicalChain) {
-      Reverter.repairLexicalChain(
-          *const_cast<DeclContext *>(D->getLexicalDeclContext()), ID);
-      RepairedLexicalContexts.insert(D->getLexicalDeclContext());
+    if (const DeclContext *LexicalDC = D->getLexicalDeclContext();
+        D->isImplicit() && !RepairedLexicalContexts.count(LexicalDC)) {
+      Reverter.repairLexicalChain(*const_cast<DeclContext *>(LexicalDC), ID);
+      RepairedLexicalContexts.insert(LexicalDC);
     }
   }
 };
@@ -814,23 +796,35 @@ void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
     if (Spec) {
       Tracker.commitFootprint(
           Spec, MutationType::SpecInfo, ID,
-          DeclStateReverter::createSpecializationFootprint(*Spec));
+          DeclStateReverter::createFootprint<SpecializationFootprint>(*Spec));
       // Member declarations are instantiated eagerly with the class
       // definition regardless of TSK; only their definitions defer. Walk
       // them now so each member's MemberSpecializationInfo is tracked from
       // the moment it exists.
       if (IsNew && Spec->getSpecializationKind() == TSK_ImplicitInstantiation)
         commitMembers(ID, cast<DeclContext>(Spec));
+      if (IsNew)
+        registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
+      else
+        settleIfClosed(Spec, DeclShape::Class,
+                       uint32_t(MutationType::SpecInfo));
     }
   }
 
   if (Rec.has(ClassMutation::MemberSpecInfo)) {
     // A nested member of a class template -- its MSI's kind or
     // point-of-instantiation changed.
-    if (RD->getMemberSpecializationInfo())
+    if (RD->getMemberSpecializationInfo()) {
       Tracker.commitFootprint(
           RD, MutationType::MemberSpecInfo, ID,
-          DeclStateReverter::createMemberSpecializationFootprint(*RD));
+          DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
+              *RD));
+      if (IsNew)
+        registerLiveVerification(RD, uint32_t(MutationType::MemberSpecInfo));
+      else
+        settleIfClosed(RD, DeclShape::Class,
+                       uint32_t(MutationType::MemberSpecInfo));
+    }
   }
 }
 
@@ -862,14 +856,26 @@ void PTUMutationActions::commitFunction(PTUID ID, const FunctionDecl *FD,
            "SpecInfoChanged on a function with no specialization info");
     Tracker.commitFootprint(
         FD, MutationType::SpecInfo, ID,
-        DeclStateReverter::createFunctionSpecializationFootprint(*FD));
+        DeclStateReverter::createFootprint<FunctionSpecializationFootprint>(
+            *FD));
+    if (IsNew)
+      registerLiveVerification(FD, uint32_t(MutationType::SpecInfo));
+    else
+      settleIfClosed(FD, DeclShape::Function, uint32_t(MutationType::SpecInfo));
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
-    if (FD->getMemberSpecializationInfo())
+    if (FD->getMemberSpecializationInfo()) {
       Tracker.commitFootprint(
           FD, MutationType::MemberSpecInfo, ID,
-          DeclStateReverter::createMemberSpecializationFootprint(*FD));
+          DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
+              *FD));
+      if (IsNew)
+        registerLiveVerification(FD, uint32_t(MutationType::MemberSpecInfo));
+      else
+        settleIfClosed(FD, DeclShape::Function,
+                       uint32_t(MutationType::MemberSpecInfo));
+    }
   }
 }
 
@@ -909,14 +915,25 @@ void PTUMutationActions::commitVar(PTUID ID, const VarDecl *VD,
     const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
     Tracker.commitFootprint(
         Spec, MutationType::SpecInfo, ID,
-        DeclStateReverter::createVarSpecializationFootprint(*Spec));
+        DeclStateReverter::createFootprint<VarSpecializationFootprint>(*Spec));
+    if (IsNew)
+      registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
+    else
+      settleIfClosed(Spec, DeclShape::Var, uint32_t(MutationType::SpecInfo));
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
-    if (VD->getMemberSpecializationInfo())
+    if (VD->getMemberSpecializationInfo()) {
       Tracker.commitFootprint(
           VD, MutationType::MemberSpecInfo, ID,
-          DeclStateReverter::createMemberSpecializationFootprint(*VD));
+          DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
+              *VD));
+      if (IsNew)
+        registerLiveVerification(VD, uint32_t(MutationType::MemberSpecInfo));
+      else
+        settleIfClosed(VD, DeclShape::Var,
+                       uint32_t(MutationType::MemberSpecInfo));
+    }
   }
 }
 
@@ -941,24 +958,23 @@ void PTUMutationActions::commitEnum(PTUID ID, const EnumDecl *ED,
   if (Rec.has(MutationType::MemberSpecInfo)) {
     // A scoped member enumeration of a class template -- instantiated with
     // the enclosing specialization, carrying MSI back to the pattern enum.
-    if (ED->getMemberSpecializationInfo())
+    if (ED->getMemberSpecializationInfo()) {
       Tracker.commitFootprint(
           ED, MutationType::MemberSpecInfo, ID,
-          DeclStateReverter::createMemberSpecializationFootprint(*ED));
+          DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
+              *ED));
+      if (IsNew)
+        registerLiveVerification(ED, uint32_t(MutationType::MemberSpecInfo));
+      else
+        settleIfClosed(ED, DeclShape::Enum,
+                       uint32_t(MutationType::MemberSpecInfo));
+    }
   }
 }
 
 void PTUMutationActions::commitTemplate(PTUID ID,
                                         const RedeclarableTemplateDecl *RT,
                                         MutationRecord &Rec, bool IsNew) {
-  // HiddenMutationTracker keeps both flags per declaration, and Cur.Mutations
-  // (Rec, per PTU) already tells us whether this PTU confirmed it, which is
-  // exactly what restoreTemplate needs, just like restoreVar. Handle both
-  // flags the same way: IsNew registers the declaration for a future sweep;
-  // otherwise, this is the confirmation from SweepTracker::sweep()'s
-  // OnConfirmed callback. Once either flag is true, it is final—
-  // Common/CanonInjectedTST won't reset it on their own—so settle it now
-  // instead of waiting for another sweep.
   const RedeclarableTemplateDecl *RTCanon = RT->getCanonicalDecl();
   if (Rec.has(MutationType::TemplateCommon)) {
     if (IsNew)
@@ -992,21 +1008,17 @@ void PTUMutationActions::commitTypedef(PTUID ID, const TypedefNameDecl *TD,
 void PTUMutationActions::commitDecl(PTUID ID, const Decl *D,
                                     MutationRecord &Rec, bool IsNew) {
   switch (Rec.S) {
-#define PTU_SHAPE(NAME, TYPE)                                                  \
+#define DECL_SHAPE(NAME, TYPE)                                                 \
   case DeclShape::NAME:                                                        \
     commit##NAME(ID, cast<TYPE>(D), Rec, IsNew);                               \
     break;
-    PTU_SHAPES
-#undef PTU_SHAPE
+    DECL_SHAPES
+#undef DECL_SHAPE
   case DeclShape::None:
     break;
   }
 }
 
-// respect the LIFO so only current inside map not commited can be commited
-// not randon PTUID
-// global map info shouldn't be commited before only added here. not note*
-// time.
 void PTUMutationActions::commit(TranslationUnitDecl *ThisTU) {
   PTUStateInfo &Cur = Tracker.current();
   assert(!Cur.Commited);
@@ -1068,8 +1080,9 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
     if (const SpecializationFootprint *Restored =
             Tracker.getFootprints().mostRecent<SpecializationFootprint>(
                 Spec, MutationType::SpecInfo))
-      DeclStateReverter::restoreSpecializationFootprint(
+      DeclStateReverter::restoreFootprint<SpecializationFootprint>(
           *Restored, *const_cast<ClassTemplateSpecializationDecl *>(Spec));
+    registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -1079,8 +1092,9 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
     if (const MemberSpecializationFootprint *Restored =
             Tracker.getFootprints().mostRecent<MemberSpecializationFootprint>(
                 RD, MutationType::MemberSpecInfo))
-      DeclStateReverter::restoreMemberSpecializationFootprint(
+      DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
           *Restored, *const_cast<CXXRecordDecl *>(RD));
+    registerLiveVerification(RD, uint32_t(MutationType::MemberSpecInfo));
   }
 }
 
@@ -1111,19 +1125,21 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
     assert(FD->getTemplateSpecializationKind() != TSK_Undeclared &&
            "SpecInfoChanged on a function with no specialization info");
     if (const auto *Restored =
-            Tracker.Footprints.mostRecent<FunctionSpecializationFootprint>(
+            Tracker.getFootprints().mostRecent<FunctionSpecializationFootprint>(
                 FD, MutationType::SpecInfo))
-      DeclStateReverter::restoreFunctionSpecializationFootprint(
+      DeclStateReverter::restoreFootprint<FunctionSpecializationFootprint>(
           *Restored, *const_cast<FunctionDecl *>(FD));
+    registerLiveVerification(FD, uint32_t(MutationType::SpecInfo));
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
     assert(FD->getMemberSpecializationInfo());
     if (const auto *Restored =
-            Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+            Tracker.getFootprints().mostRecent<MemberSpecializationFootprint>(
                 FD, MutationType::MemberSpecInfo))
-      DeclStateReverter::restoreMemberSpecializationFootprint(
+      DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
           *Restored, *const_cast<FunctionDecl *>(FD));
+    registerLiveVerification(FD, uint32_t(MutationType::MemberSpecInfo));
   }
 }
 
@@ -1171,56 +1187,46 @@ void PTUMutationActions::restoreTypedef(PTUID ID, const TypedefNameDecl *TD,
 
 void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
                                     MutationRecord &Rec) {
-
-  using VarMutation = MutationType;
-
-  // EvaluatedStmt::WasEvaluated is write-once, so its previous state is always
-  // null here. If it made it into Rec, this PTU had confirmed it as evaluated.
-  // Reverting it is therefore just a reset, not a snapshot restore – the
-  // original initializer expression in Eval->Value stays untouched; we only
-  // clear the cached result.
-  if (Rec.has(VarMutation::EvaluatedValue)) {
-    // VD itself survives this rollback (only its cached value was this
-    // PTU's doing) -- reset the live field, and re-register with
-    // SweepTracker since DeclNeedingTracking will flag this as open
-    // again and some later PTU could re-trigger evaluation.
+  // EvaluatedStmt::WasEvaluated is write-once, so a recorded entry means this
+  // PTU confirmed the evaluation.
+  if (Rec.has(MutationType::EvaluatedValue)) {
+    // VD cached evaluation came from this PTU.
+    // Reset it and track it again so a later PTU can re-trigger evaluation.
     if (EvaluatedStmt *Eval = VD->getEvaluatedStmt()) {
       Eval->WasEvaluated = false;
       Eval->Evaluated = APValue();
     }
-    HiddenMutationTracker.track(VD, uint32_t(VarMutation::EvaluatedValue));
+    HiddenMutationTracker.track(VD, uint32_t(MutationType::EvaluatedValue));
   }
 
-  if (Rec.has(VarMutation::DefinitionInstantiate)) {
+  if (Rec.has(MutationType::DefinitionInstantiate)) {
     DeclStateReverter::revertDefinitionArrival(*const_cast<VarDecl *>(VD));
   }
 
-  if (Rec.has(VarMutation::SpecInfo)) {
-    // Unlike functions, a variable specialization IS a distinct type.
+  if (Rec.has(MutationType::SpecInfo)) {
     const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
     if (const auto *Restored =
             Tracker.getFootprints().mostRecent<VarSpecializationFootprint>(
                 Spec, MutationType::SpecInfo))
-      DeclStateReverter::restoreVarSpecializationFootprint(
+      DeclStateReverter::restoreFootprint<VarSpecializationFootprint>(
           *Restored, *const_cast<VarTemplateSpecializationDecl *>(Spec));
+    registerLiveVerification(VD, uint32_t(MutationType::SpecInfo));
   }
 
-  if (Rec.has(VarMutation::MemberSpecInfo)) {
+  if (Rec.has(MutationType::MemberSpecInfo)) {
     assert(VD->getMemberSpecializationInfo());
     if (const auto *Restored =
             Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
                 VD, MutationType::MemberSpecInfo))
-      DeclStateReverter::restoreMemberSpecializationFootprint(
+      DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
           *Restored, *const_cast<VarDecl *>(VD));
+    registerLiveVerification(VD, uint32_t(MutationType::MemberSpecInfo));
   }
 }
 
 void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
                                      MutationRecord &Rec) {
 
-  // Like a class, an EnumDecl's TypeForDecl is shared across its whole
-  // redeclaration chain (`enum class E : int;` forward-declared, then
-  // defined later), so every redecl's cached pointer needs checking.
   if (Rec.has(MutationType::TypeForDecl)) {
     // for (const TagDecl *Redecl : ED->redecls()) {
     //   const auto *Last = Tracker.Footprints.mostRecent<const Type *>(
@@ -1235,36 +1241,52 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
   //     *>(ED));
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
-    // A scoped member enumeration of a class template -- instantiated with
-    // the enclosing specialization, carrying MSI back to the pattern enum.
     assert(ED->getMemberSpecializationInfo());
     if (const auto *Restored =
             Tracker.getFootprints().mostRecent<MemberSpecializationFootprint>(
                 ED, MutationType::MemberSpecInfo))
-      DeclStateReverter::restoreMemberSpecializationFootprint(
+      DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
           *Restored, *const_cast<EnumDecl *>(ED));
+
+    registerLiveVerification(ED, uint32_t(MutationType::MemberSpecInfo));
   }
 }
 
 void PTUMutationActions::restoreDecl(PTUID ID, const Decl *D,
                                      MutationRecord &Rec) {
   switch (Rec.S) {
-#define PTU_SHAPE(NAME, TYPE)                                                  \
+#define DECL_SHAPE(NAME, TYPE)                                                 \
   case DeclShape::NAME:                                                        \
     restore##NAME(ID, cast<TYPE>(D), Rec);                                     \
     break;
-    PTU_SHAPES
-#undef PTU_SHAPE
+    DECL_SHAPES
+#undef DECL_SHAPE
   case DeclShape::None:
     break;
   }
 }
-#undef PTU_SHAPES
+#undef DECL_SHAPES
+
+void PTUMutationActions::restoreSpecialMemberCache(PTUID ID) {
+  PTUStateInfo &Cur = Tracker.current();
+  if (!Cur.HadImplicitCXXMember)
+    return;
+
+  Sema &SemaRef = Tracker.getSema();
+  llvm::SmallVector<Sema::SpecialMemberCacheKey, 8> Stale;
+  for (auto &Entry : SemaRef.SpecialMemberCache) {
+    CXXMethodDecl *MD = Entry.second.getMethod();
+    if (MD && (Tracker.isFromThisPTU(MD, ID) || Cur.ImplicitDecls.contains(MD)))
+      Stale.push_back(Entry.first);
+  }
+  for (const auto &Key : Stale)
+    SemaRef.SpecialMemberCache.erase(Key);
+}
 
 void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
   PTUStateInfo &Cur = Tracker.current();
-  assert(ThisTU == Cur.ThisPTU);
-  //   TranslationUnitDecl *ThisTU = Cur.ThisPTU;
+  assert((!Cur.Commited || ThisTU == Cur.ThisPTU) &&
+         "MostRecentTU doesn't match the TU recorded at commit time");
   PTUID ID = Cur.ID;
   if (Cur.Commited)
     Tracker.undoLastEntries();
@@ -1288,24 +1310,11 @@ void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
 
   walkDecls(ThisTU, Policy);
 
-  // Sema::SpecialMemberCache (public) caches, per (RD, kind+qualifiers), the
-  // CXXMethodDecl* a prior LookupSpecialMember() call resolved to -- and on
-  // a cache hit it returns that decl directly, with no re-validation at all
-  // (SemaLookup.cpp). Any entry whose cached method was created by the PTU
-  // being rolled back is now stale, so purge it here rather than trying to
-  // reconstruct which of the (RD, kind) keys it could occupy.
-  {
-    Sema &SemaRef = Tracker.getSema();
-    llvm::SmallVector<Sema::SpecialMemberCacheKey, 8> Stale;
-    for (auto &Entry : SemaRef.SpecialMemberCache) {
-      CXXMethodDecl *MD = Entry.second.getMethod();
-      if (MD &&
-          (Tracker.isFromThisPTU(MD, ID) || Cur.ImplicitDecls.contains(MD)))
-        Stale.push_back(Entry.first);
-    }
-    for (const auto &Key : Stale)
-      SemaRef.SpecialMemberCache.erase(Key);
-  }
+  // SpecialMemberCache stores the CXXMethodDecl* resolved by a previous
+  // LookupSpecialMember() call and returns it directly on a cache hit.
+  // If that method was created by a PTU that is being rolled back, the entry
+  // is stale, so remove it here.
+  restoreSpecialMemberCache(ID);
 
   // Must be last: Cur/ID/ThisTU are all references into (or derived from)
   // the PTU this call retires, and popCurrentPTU() destroys that entry.
@@ -1331,16 +1340,14 @@ void PTUMutationRecorder::CompletedTagDefinition(const TagDecl *D) {
       Cur.ImplicitDecls.insert(D);
     return;
   }
-  // Predates this PTU: a genuine cross-PTU mutation -- needs the full
-  // definition-data footprint captured in commit().
-  // Cur.TouchedTagDecls.insert(D);
+
   DeclShape S = classifyShape(D);
 
-  /// doing this for memspec because we are not sure that if this cause member
-  /// spec mutation or not we can't put compare check here as well since
-  /// notifer never guarranty that will called before mutation or later. so
-  /// this check about mutation will be handled in verification layer
   Cur.noteMutated(D, S, MutationType::DefinitionInstantiate);
+
+  /// Handle this for MemberSpec because the notifier does not guarantee whether
+  /// it fires before or after the mutation. We cannot reliably compare the
+  /// state here, so mutation detection is deferred to the verification layer.
   if (S == DeclShape::Enum) {
     if (const EnumDecl *ED = dyn_cast<EnumDecl>(D);
         ED && ED->getMemberSpecializationInfo())
@@ -1362,10 +1369,7 @@ void PTUMutationRecorder::AddedVisibleDecl(const DeclContext *DC,
 
   if (Tracker.isFromThisPTU(DC, Cur.ID) || Cur.TouchedDC.contains(DC))
     return;
-  //   auto Origin =
-  //       Tracker.getPTUSlabCheckpoints().attribute(Tracker.Ctx, DC);
-  //   assert(Origin && "must have existing PTUID");
-  //   assert(*Origin != Cur.ID);
+
   Cur.TouchedDC.insert(DC);
 }
 
@@ -1379,8 +1383,6 @@ void PTUMutationRecorder::AddedCXXImplicitMember(const CXXRecordDecl *RD,
 
   if (D->isImplicit())
     Cur.ImplicitDecls.insert(D);
-  // cover this part as well
-  // if (RD->MemberInfo and D is implcit plus has MSIInfo)
 
   // cover only not from this ptu.
   if (Tracker.isFromThisPTU(RD, Cur.ID))
@@ -1467,9 +1469,6 @@ void PTUMutationRecorder::InstantiationRequested(const ValueDecl *D) {
     if (FD->getMemberSpecializationInfo())
       K = MutationType::MemberSpecInfo;
     else if (FD->getTemplateSpecializationKind() != TSK_Undeclared)
-      // A function template specialization is a plain FunctionDecl
-      // carrying FunctionTemplateSpecializationInfo -- there is no
-      // distinct decl type to dyn_cast to, unlike the variable case.
       K = MutationType::SpecInfo;
     else
       return; // neither -- nothing this notifier can be about
@@ -1488,11 +1487,7 @@ void PTUMutationRecorder::InstantiationRequested(const ValueDecl *D) {
   Cur.noteMutated(D, S, K);
 }
 
-// Each (Owner, Kind) key written by this PTU has its trailing entries
-// removed in one uniform call. There’s no need for per-node-type
-// release() or pool bookkeeping: FootprintStore’s SmallVector-per-key
-// storage manages the memory normally. Once a key’s history is empty,
-// removeFrom erases the key entirely, just as it always has.
+// Remove all entries for each (Owner, Kind) key written by this PTU.
 void IncrementalStateTracker::undoLastEntries() {
   PTUStateInfo &Cur = current();
   PTUID ID = Cur.ID;
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index fac67bf00ed14..2c5a4d8489dc1 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -333,8 +333,6 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
 
   // Lookup alone is not enough: the redeclaration chain still reaches these.
   withdrawMostRecentTU(MostRecentTU);
-  // RepairRedeclChain(MostRecentTU, MostRecentTU);
-  // S.getASTContext().setTranslationUnitDecl(MostRecentTU->getPreviousDecl());
 }
 
 PartialTranslationUnit &

>From e464102e3a8b32a0e06990333d2121dd1ae189b1 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 30 Sep 2026 14:28:31 +0530
Subject: [PATCH 11/13] Make improvements and add meaningful comments

---
 .../include/clang/Interpreter/ErrorRecovery.h |  72 ++-
 clang/lib/Interpreter/ErrRecovery.cpp         | 528 +++++++++++-------
 clang/lib/Interpreter/Interpreter.cpp         |   4 +-
 llvm/include/llvm/Support/Allocator.h         |  95 ++--
 4 files changed, 389 insertions(+), 310 deletions(-)

diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index b8365efc41968..85f7ffef1e8a0 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -7,6 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 //
+// This header provides the main error-recovery component for supporting
+// error recovery in clang-repl.
 //
 //===----------------------------------------------------------------------===//
 
@@ -27,16 +29,8 @@ class Sema;
 /// Index of a per PTU State.
 using PTUID = unsigned;
 
-// A snapshot of the handful of DefinitionData bits/bitfields that can
-// change after a class (or class template specialization -- the two are
-// handled identically here since a specialization is non-dependent by the
-// time it's instantiated) is otherwise "done" being defined.
-//
-// These fields only move when the compiler generates implicit special
-// members.
-//
-// This footprint exists purely to undo the effects of implicit special
-// member generation.
+/// A snapshot of the DefinitionData bits and bitfields that can change
+/// after a class gets a special implicit member added.
 struct DefinitionDataFootprint {
 #define FIELD(Name, Width, Merge) unsigned Name : Width;
 #include "clang/AST/CXXRecordDeclDefinitionBits.def"
@@ -286,9 +280,7 @@ class DeclStateReverter {
     FP.restore(Owner);
   }
 
-  static void restoreDefinitionAndRevertDC(CXXRecordDecl &RD);
-
-  static void revertDefinitionArrival(Decl &D);
+  static void revertDefinitionArrival(Decl *D);
 
   // True if Common could still be created later -- i.e. nobody has called
   // getCommonPtr() anywhere in this template's redecl chain yet.
@@ -314,10 +306,7 @@ class DeclStateReverter {
   }
 
   // Specializations are appended in commit order, and rollback is strictly
-  // LIFO (only the most recently committed PTU is ever rolled back). So all
-  // specializations added by a given PTU form a trailing run at the back of
-  // the FoldingSetVector. Popping entries while the back entry belongs to
-  // this PTU is therefore sufficient.
+  // LIFO (only the most recently committed PTU is ever rolled back).
   static void removeSpecializations(PTUCheckpointLedger &Ledger,
                                     const RedeclarableTemplateDecl *TD,
                                     PTUID ID);
@@ -441,7 +430,7 @@ class PTUCheckpointLedger {
 struct MutationRecord {
   enum class DeclShape : uint16_t {
     None = 0,
-    // Base shapes -- what the decl fundamentally IS. Exactly one is set.
+    // Base shapes -- what the decl fundamentally is. Exactly one is set.
     Class = 1 << 0,    // CXXRecordDecl/TagDecl: DefinitionData, TypeForDecl
     Function = 1 << 1, // FunctionDecl: exception spec, deduced return, body
     Var = 1 << 2,      // VarDecl: cached constant-eval result
@@ -538,14 +527,7 @@ struct PTUStateInfo {
 
   llvm::SmallPtrSet<const Decl *, 4> ImplicitDecls;
 
-  /// Set exactly when AddedCXXImplicitMember fires this PTU -- narrower
-  /// than ImplicitDecls above, which is a shared superset populated by
-  /// many unrelated listener paths too (template instantiation, etc.).
-  /// This is the one listener whose firing can leave a stale
-  /// Sema::SpecialMemberCache entry behind, so it's what
-  /// purgeStaleSpecialMemberCache actually needs to gate on -- gating on
-  /// ImplicitDecls instead would trigger that scan on almost every PTU,
-  /// defeating the point of the gate.
+  /// Set exactly when AddedCXXImplicitMember fires this PTU.
   bool HadImplicitCXXMember = false;
 
   /// here touched info mean other this belongs to other PTUs;
@@ -609,7 +591,7 @@ class SweepTracker {
     return It != Active.end() && (It->second & Flag);
   }
 
-  void settle(const Decl *D, uint32_t Flag) {
+  void untrack(const Decl *D, uint32_t Flag) {
     auto It = Active.find(D);
     if (It == Active.end())
       return;
@@ -618,6 +600,16 @@ class SweepTracker {
       Active.erase(It);
   }
 
+  // removes every Active entry whose decl matches Predicate.
+  template <typename PredicateFn> void untrackIf(PredicateFn Predicate) {
+    llvm::SmallVector<const Decl *, 8> Stale;
+    for (auto &Entry : Active)
+      if (Predicate(Entry.getFirst()))
+        Stale.push_back(Entry.getFirst());
+    for (const Decl *D : Stale)
+      Active.erase(D);
+  }
+
   // Call at commit/restore time. OnConfirmed is invoked as
   // (const Decl *D, DeclShape S, uint32_t ConfirmedKinds) for every decl
   // that had something newly confirmed this sweep.
@@ -640,8 +632,7 @@ class SweepTracker {
 // - For the current PTU, collect newly added declarations that are
 //   modifiable/visible to other PTUs. Local-only declarations are not exposed.
 //
-// In short, this keeps the shared declaration state in sync across PTUs while
-// keeping declarations that are local to the current PTU private.
+// In short, this keeps the shared declaration state in sync across PTUs.
 class IncrementalStateTracker {
 private:
   ASTContext &Ctx;
@@ -666,10 +657,9 @@ class IncrementalStateTracker {
   IncrementalStateTracker(ASTContext &Ctx, Sema &S)
       : Ctx(Ctx), SemaRef(S), PTUSlabCheckpoints(Ctx) {}
 
-  // Writes a new value and records that this PTU touched (Owner, K), so
-  // undoLastEntries() knows what to remove if the PTU is rolled back
-  // without committing. mostRecent() doesn't need any bookkeeping, so it
-  // reads directly from Footprints via Tracker.Footprints.mostRecent<T>(...).
+  // Writes a new value to FootprintStore and records that this PTU touched
+  // (Owner, K), so undoLastEntries() knows what to remove if the PTU is rolled
+  // back without committing.
   template <typename T>
   void commitFootprint(const Decl *Owner, MutationType K, PTUID ID,
                        const T &Fresh) {
@@ -728,15 +718,18 @@ class PTUMutationActions {
         Reverter(Tracker.getPTUSlabCheckpoints()) {}
 
   template <typename DeclStatePolicyT>
-  void walkDecls(const DeclContext *DC, DeclStatePolicyT &Proxy);
+  void walkDecls(const DeclContext *DC, DeclStatePolicyT &Policy);
 
   uint32_t DeclNeedingTracking(DeclShape S, const Decl *D);
 
   uint32_t confirmMutation(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
 
-  void registerLiveVerification(const Decl *D, uint32_t Kinds);
+  void trackForSweep(const Decl *D, uint32_t Kinds);
+
+  void untrackIfClosed(const Decl *D, DeclShape S, uint32_t Confirmed);
 
-  void settleIfClosed(const Decl *D, DeclShape S, uint32_t Confirmed);
+  void syncSweepTracking(const Decl *D, DeclShape S, uint32_t Kinds,
+                         bool IsNew);
 
 private:
   void commitMembers(PTUID ID, const DeclContext *Members);
@@ -783,10 +776,9 @@ class PTUMutationActions {
   void restore(TranslationUnitDecl *MostRecentTU);
 
 private:
-  // Sema::SpecialMemberCache, per (RD,
-  // kind+qualifiers), the CXXMethodDecl* a prior LookupSpecialMember()
-  // call resolved to, with no re-validation on a cache hit. remove every
-  // entry whose cached method belongs to the PTU being rolled back.
+  // Sema::SpecialMemberCache stores the CXXMethodDecl* resolved by a previous
+  // LookupSpecialMember() call for each (RD, kind+qualifiers). Remove entries
+  // whose cached method belongs to the PTU being rolled back.
   void restoreSpecialMemberCache(PTUID ID);
 };
 
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index 509d0d0a40668..fc9182f16aaa0 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -59,29 +59,52 @@ void DeclStateReverter::restoreDefinitionDataFootprint(
 #include "clang/AST/CXXRecordDeclDefinitionBits.def"
 }
 
-void DeclStateReverter::restoreDefinitionAndRevertDC(CXXRecordDecl &RD) {
-  // 1. DefinitionData pointer -- back to null.
-  RD.DefinitionData = nullptr;
-
-  // 2. DeclContext member list -- back to empty.
-  clearDeclContextChain(RD);
-  DeclContext *Primary = RD.getDeclContext()->getPrimaryContext();
-  if (StoredDeclsMap *Map = Primary->getLookupPtr())
-    Map->clear();
-
-  // 3. TagDecl completion bits -- back to "never started". Flipped by
-  // startDefinition()/completeDefinition().
-  clearBeingDefined(RD);
-  RD.setCompleteDefinition(false);
-}
-
-void DeclStateReverter::revertDefinitionArrival(Decl &D) {
-  if (auto *FD = dyn_cast<FunctionDecl>(&D))
+void DeclStateReverter::revertDefinitionArrival(Decl *D) {
+  if (auto *FD = dyn_cast<FunctionDecl>(D)) {
     FD->setBody(nullptr);
-  else if (auto *VD = dyn_cast<VarDecl>(&D))
+    // Reset the body state so a later PTU sees this as not instantiated.
+    FD->setWillHaveBody(false);
+
+    // The reverted body may have caused a substitution failure to mark
+    // the function invalid. Clear that state as well.
+    FD->setInvalidDecl(false);
+  } else if (auto *VD = dyn_cast<VarDecl>(D)) {
     VD->setInit(nullptr);
-  else if (auto *Field = dyn_cast<FieldDecl>(&D))
+    VD->setInvalidDecl(false);
+  } else if (auto *Field = dyn_cast<FieldDecl>(D))
     Field->setInClassInitializer(nullptr);
+  else if (auto *ED = dyn_cast<EnumDecl>(D)) {
+    // Undo EnumDecl::completeDefinition() writes. PromotionType and both
+    // bit-count fields are always overwritten during completion, so they can
+    // be reset unconditionally.
+    ED->setPromotionType(QualType());
+    // resetEnumBits(*ED);
+    // EnumDecl::setNumPositiveBits/setNumNegativeBits -- private.
+    // ED->setNumPositiveBits(0);
+    // ED->setNumNegativeBits(0);
+
+    // completeDefinition() only sets IntegerType for non-fixed enums. A fixed
+    // enum may already have an IntegerType from its forward declaration, which
+    // must be preserved.
+    if (!ED->isFixed())
+      ED->setIntegerType(QualType());
+    ED->setCompleteDefinition(false);
+    clearBeingDefined(*ED);
+  } else if (auto *RD = dyn_cast<CXXRecordDecl>(D)) {
+    // 1. DefinitionData pointer -- back to null.
+    RD->DefinitionData = nullptr;
+
+    // 2. DeclContext member list -- back to empty.
+    clearDeclContextChain(*RD);
+    DeclContext *Primary = RD->getDeclContext()->getPrimaryContext();
+    if (StoredDeclsMap *Map = Primary->getLookupPtr())
+      Map->clear();
+
+    // 3. TagDecl completion bits -- back to "never started". Flipped by
+    // startDefinition()/completeDefinition().
+    clearBeingDefined(*RD);
+    RD->setCompleteDefinition(false);
+  }
 }
 
 void DeclStateReverter::removeSpecializations(
@@ -276,19 +299,18 @@ static DeclShape classifyShape(const Decl *D) {
     return DeclShape::None;
 }
 
-/// Called once when a decl is first seen. This only sets up the possible
-/// MutationRecord kinds; it does not detect the mutation itself.
-///
-/// Add a kind here only if we need to do something before the mutation:
-/// - save the old value because it will be overwritten before the listener
-///   runs, or
-/// - start hidden tracking because there is no listener for that change.
-///
-/// If the listener itself is enough to detect the change, don't add it here.
-/// noteMutated() will create the record when the change actually happens.
+/// This is called when a decl is created or seen for the first time. It also
+/// checks whether the decl is still in a mutation-sensitive state, meaning that
+/// a later PTU can modify it. Because of this, we need to track its state. This
+/// function provides a single place to decide whether a decl needs to be
+/// committed or not. It is called from DeclStateCommitPolicy::process to
+/// determine whether a new decl needs to be tracked.
 ///
-/// Explicit specializations are terminal, so their SpecInfo/MemberSpecInfo
-/// does not need to be tracked.
+/// This requires DeclShape S and Decl to determine which mutation types
+/// apply to the shape and whether they need to be tracked.
+/// In the future, if a new mutation site is found for a shape, it should be
+/// added here along with the logic to determine whether the decl is still in an
+/// open, modifiable state.
 uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
   switch (S) {
   case DeclShape::Class: {
@@ -296,6 +318,8 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
     uint32_t Kinds = 0;
     // TypeForDecl is written twice for TagDecls. Keep tracking until both
     // writes are done.
+    // Requires friend access to TypeDecl and was added for
+    // memory restoration, so we can keep it commented out for now.
     // if (const Type *T = DeclStateReverter::getRawTypeForDecl(RD);
     //     !T || T->isCanonicalUnqualified())
     //   Kinds |= uint32_t(MutationType::TypeForDecl);
@@ -313,6 +337,12 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
         Kinds |= uint32_t(MutationType::DefinitionData);
     }
     if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+      // If SpecializationKind == TSK_ExplicitSpecialization, there is less
+      // chance that the info will be modified later, so we are all set and
+      // don't need to track it.
+      // A specialization's Kind/point-of-instantiation is overwritten in place,
+      // with no separate record of the previous value. The footprint chain is
+      // the only place where the previous value is stored.
       if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization)
         Kinds |= uint32_t(MutationType::SpecInfo);
     } else if (const auto *MSI = RD->getMemberSpecializationInfo()) {
@@ -325,7 +355,7 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
     const auto *FD = cast<FunctionDecl>(D);
     uint32_t Kinds = 0;
     const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
-    // Exception-spec resolution changes the type before the listener runs,
+    // Exception-spec resolution changes the type before the listener notify,
     // so we need the old type saved beforehand.
     if (FPT && (FPT->getExceptionSpecType() == EST_Unevaluated ||
                 FPT->getExceptionSpecType() == EST_Uninstantiated ||
@@ -333,14 +363,11 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
                 FPT->getExceptionSpecType() == EST_Unparsed))
       Kinds |= uint32_t(MutationType::ExceptionSpec);
     if (FD->getReturnType()->isUndeducedType())
-      // Deduced return type works the same way: the type is changed before
-      // the listener runs, so save it beforehand.
+      // Deduced return type is changed before
+      // the listener notify, so save it beforehand.
       Kinds |= uint32_t(MutationType::DeducedReturnType);
     if (FD->getTemplateSpecializationInfo() &&
         FD->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
-      // A specialization’s Kind/point-of-instantiation is overwritten in place,
-      // with no separate record of the old value. The footprint chain is the
-      // only place where the previous value is preserved.
       Kinds |= uint32_t(MutationType::SpecInfo);
     if (const auto *MSI = FD->getMemberSpecializationInfo()) {
       if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
@@ -353,7 +380,6 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
     const auto *VD = cast<VarDecl>(D);
     uint32_t Kinds = 0;
     if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
-      // Same terminal-state exclusion as the Class/Function cases above.
       if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization)
         Kinds |= uint32_t(MutationType::SpecInfo);
     } else if (const auto *MSI = VD->getMemberSpecializationInfo()) {
@@ -364,11 +390,7 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
     // constant-evaluated, which can happen in any PTU long after the VarDecl
     // was created. There is no ASTMutationListener event for this
     // (VarDecl::evaluateValueImpl doesn’t notify anything), so this is a
-    // hidden kind that we have to check live here, just like
-    // Shape::Template’s CommonCreated.
-    //
-    // getEvaluatedStmt() is a pure read – unlike evaluateValue(), it doesn’t
-    // trigger evaluation itself, so it’s safe to check on every classify call.
+    // hidden kind that we have to check here.
     if (const EvaluatedStmt *Eval = VD->getEvaluatedStmt();
         !Eval || !Eval->WasEvaluated)
       Kinds |= uint32_t(MutationType::EvaluatedValue);
@@ -379,16 +401,12 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
     const auto *ED = cast<EnumDecl>(D);
     uint32_t Kinds = 0;
     // TypeForDecl: EnumDecl is also a TagDecl, so the same two-write pattern
-    // and “still open” condition as the Class case above apply here – see
-    // its comment for the details.
-    //
+    // and "still open" condition as in the Class case above apply here. See
+    // its comment for details.
     // if (const Type *T = DeclStateReverter::getRawTypeForDecl(ED);
     //     !T || T->isCanonicalUnqualified())
     //   Kinds |= uint32_t(MutationType::TypeForDecl);
     //
-    // MSI-backed part: for an ordinary enum, its own completion is the
-    // redeclaration-creates-a-new-object case. EnumDecl is Redeclarable just
-    // like CXXRecordDecl, so the same reasoning as Function/Var applies here.
     if (const auto *MSI = ED->getMemberSpecializationInfo()) {
       // Same terminal-state exclusion as the other MSI-backed cases above.
       if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
@@ -419,7 +437,7 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
   }
   case DeclShape::Typedef: {
     // TypedefDecl/TypeAliasDecl only -- ASTContext::getTypedefType() has its
-    // own guard (if (Decl->TypeForDecl) // return), so this is effectively
+    // own guard (if (Decl->TypeForDecl) // return), so this is
     // write-once: once non-null, TypeForDecl
     // never changes again for this decl.
     // const auto *TD = cast<TypedefNameDecl>(D);
@@ -448,13 +466,23 @@ static FootprintT Footprint(const OwnerT &Owner) {
   return FP;
 }
 
-/// The single place to check whether a kind actually changed.
+/// The single place to check whether a kind has actually changed.
+///
+/// This handles both listener-less kinds and listener-unconfirmed mutations
+/// (currently SpecInfo and MemberSpecInfo). Mutations can be detected by
+/// both SweepTracker and the listener re-check. SweepTracker tracks hidden
+/// and listener-unconfirmed mutations in case the listener misses them. If the
+/// listener catches a mutation, it is removed from SweepTracker; see
+/// PTUStateInfo::verifyMutations. both use this functionality to verify
+/// mutation kind.
 ///
-/// It handles both listener-less kinds and SpecInfo/MemberSpecInfo. The
-/// latter can be checked from both the sweep and the listener re-check.
+/// This check uses the decl shape and mutation kind to determine whether the
+/// decl has changed from its previous state.
 ///
-/// Keeping everything in one function makes sure SpecInfo/MemberSpecInfo
-/// changes are caught even when the listener never fires.
+/// Similarly, if a new hidden or listener-unconfirmed mutation is introduced
+/// in DeclNeedingTracking, and we need to determine whether it changed the
+/// previous state, the corresponding logic should be added here too
+/// when the kind is categorized as a hidden mutation from the listener.
 uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
                                              uint32_t FlaggedKinds) {
   uint32_t Verified = 0;
@@ -462,7 +490,9 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
   case DeclShape::Class: {
     const auto *RD = cast<CXXRecordDecl>(D);
     if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
-      // No listener at all for this mutation.
+      // No listener at all for this mutation. why it is commented out:
+      // Requires friend access to TypeDecl and was added for
+      // memory restoration, so we can keep it commented out for now.
       // const Type *Now = DeclStateReverter::getRawTypeForDecl(RD);
       // if (differsFromLast(Tracker, RD, MutationType::TypeForDecl, Now))
       //   Verified |= uint32_t(MutationType::TypeForDecl);
@@ -490,7 +520,7 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
                           Footprint<MemberSpecializationFootprint>(*FD)))
         Verified |= uint32_t(MutationType::MemberSpecInfo);
     }
-    break; // Function carries no listener-less kind at all.
+    break;
   }
   case DeclShape::Var: {
     const auto *VD = cast<VarDecl>(D);
@@ -529,7 +559,7 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
     break;
   }
   case DeclShape::Template: {
-    // Use the canonical key. commitTemplate() tracks and settles CommonBase
+    // Use the canonical key. commitTemplate() tracks and untrack CommonBase
     // modifications, and CommonPtr is shared across the redeclaration chain.
     // Any modification to CommonPtr is reflected in the canonical declaration.
     const auto *RT = cast<RedeclarableTemplateDecl>(D)->getCanonicalDecl();
@@ -554,7 +584,6 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
     if (FlaggedKinds & uint32_t(MutationType::TypeForDecl) &&
         Tracker.getHiddenMutationTracker().isTrackedFor(
             TD, uint32_t(MutationType::TypeForDecl))) {
-      // No chain to compare against -- write-once field.
       // if (DeclStateReverter::getRawTypeForDecl(TD))
       //   Verified |= uint32_t(MutationType::TypeForDecl);
     }
@@ -566,6 +595,12 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
   return Verified;
 }
 
+/// A single function called during commit and restore (when the PTU is
+/// uncommitted).
+///
+/// This is where we check for any hidden mutations previously registered with
+/// SweepTracker. If a mutation is confirmed, we record it in the
+/// PTUStateInfo mutation set via OnConfirmed.
 template <typename OnConfirmedFn>
 void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
   for (auto &Entry : Active) {
@@ -578,43 +613,54 @@ void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
   }
 }
 
-// These kinds have listeners, but the listener alone can’t reliably confirm
-// a real change. So, compare the current value with the last known value,
-// just like we do for kinds without listeners.
-//
-// These kinds are also checked by the sweep. This is needed because the
-// listener can report a change that didn’t happen, or fail to fire when a
-// real change did happen. The sweep catches the second case.
+/// These kinds have listeners, but listeners alone cannot reliably detect
+/// real changes. Compare the current value with the last known value, as we
+/// do for kinds without listeners.
+///
+/// These kinds are also checked by the sweep to catch changes that listeners
+/// miss or report incorrectly.
+///
+/// Currently, SpecInfo/MemberSpecInfo are tracked from the listener side.
+/// This applies when the listener reports that a mutation may have happened
+/// but it has not been confirmed yet.
+///
+/// Not all hidden mutations are tracked here; they are tracked in
+/// SweepTracker. This logic applies only to listener-unconfirmed mutations.
 static constexpr uint32_t KindsNeedingVerification =
     uint32_t(MutationType::SpecInfo) | uint32_t(MutationType::MemberSpecInfo);
 
-/// Start tracking D for the mutation kinds that need live verification.
+/// Start tracking D for mutation kinds that need verification.
 /// Called when a new mutation is committed or an undone mutation is restored.
-/// The callers have already established which kinds need tracking, so no
-/// additional DeclNeedingTracking check is needed.
-void PTUMutationActions::registerLiveVerification(const Decl *D,
-                                                  uint32_t Kinds) {
-  if (uint32_t Live = Kinds & KindsNeedingVerification)
-    HiddenMutationTracker.track(D, Live);
+/// we apply this for listener unconfirmed cases.
+void PTUMutationActions::trackForSweep(const Decl *D, uint32_t Kinds) {
+  if (uint32_t Flag = Kinds & KindsNeedingVerification)
+    HiddenMutationTracker.track(D, Flag);
 }
 
-/// Stop tracking Decl D when its mutation no longer needs live verification.
-/// Called from verifyMutations() and the !IsNew SpecInfo/MemberSpecInfo
-/// paths in commitDecl. Re-check DeclNeedingTracking to match the same
-/// "is this still open?" condition used when D is first discovered:
-/// keep tracking if a later PTU can still update it; otherwise settle it.
-void PTUMutationActions::settleIfClosed(const Decl *D, DeclShape S,
-                                        uint32_t Confirmed) {
-  uint32_t Live = Confirmed & KindsNeedingVerification;
-  if (!Live)
+/// Stop tracking Decl D when it no longer needs verification.
+/// Re-check DeclNeedingTracking to see if a later PTU can still update D.
+void PTUMutationActions::untrackIfClosed(const Decl *D, DeclShape S,
+                                         uint32_t Confirmed) {
+  uint32_t Flag = Confirmed & KindsNeedingVerification;
+  if (!Flag || !HiddenMutationTracker.isTrackedFor(D, Flag))
     return;
-  uint32_t StillOpen = DeclNeedingTracking(S, D) & Live;
-  uint32_t Closed = Live & ~StillOpen;
-  if (Closed && HiddenMutationTracker.isTrackedFor(D, Closed))
-    HiddenMutationTracker.settle(D, Closed);
+  uint32_t StillOpen = DeclNeedingTracking(S, D) & Flag;
+  uint32_t Closed = Flag & ~StillOpen;
+  if (Closed)
+    HiddenMutationTracker.untrack(D, Closed);
+}
+
+// Shared sweep-tracking logic(track/untrack) for
+// commitClass/commitFunction/commitVar/commitEnum.
+void PTUMutationActions::syncSweepTracking(const Decl *D, DeclShape S,
+                                           uint32_t Kinds, bool IsNew) {
+  if (IsNew)
+    trackForSweep(D, Kinds);
+  else
+    untrackIfClosed(D, S, Kinds);
 }
 
-/// Verify recorded mutations and remove any spurious ones. If none of a
+/// Verify recorded mutations and remove any false mutation ones. If none of a
 /// decl’s mutation kinds are confirmed, remove the record entirely so
 /// restore() does not pop chain entries that were never written.
 void PTUStateInfo::verifyMutations(PTUMutationActions &Actions) {
@@ -623,9 +669,10 @@ void PTUStateInfo::verifyMutations(PTUMutationActions &Actions) {
     MutationRecord &Rec = Entry.second;
     uint32_t Flagged = Rec.MutationType & KindsNeedingVerification;
     if (!Flagged)
-      return false; // nothing here needs live verification.
+      return false;
     uint32_t Confirmed = Actions.confirmMutation(D, Rec.S, Flagged);
-    Actions.settleIfClosed(D, Rec.S, Confirmed);
+    /// untrack kind from SweepTracker if confirmed.
+    Actions.untrackIfClosed(D, Rec.S, Confirmed);
     Rec.MutationType = (Rec.MutationType & ~Flagged) | Confirmed;
     return Rec.MutationType == 0;
   });
@@ -655,13 +702,13 @@ class DeclStateCommitPolicy {
     if (S == DeclShape::None)
       return;
 
-    // we have to handle every case that if a decl need to create baseline
-    // info or if any decl has done all mutations already in this for exmpla
-    // membe spec already properly done like this kind of case so we don't
-    // create spec info blindly
+    // Handle cases where baseline info needs to be created, as well as cases
+    // where a declaration has already completed all required mutations. For
+    // example, member specs may already be fully populated, so we should avoid
+    // creating spec info blindly.
     uint32_t Kinds = Action.DeclNeedingTracking(S, D);
     if (!Kinds)
-      return; // structurally immutable -- no baseline needed
+      return;
 
     MutationRecord Rec{S, Kinds};
     Action.commitDecl(ID, D, Rec, /*IsNew=*/true);
@@ -706,9 +753,9 @@ class DeclStateUnlinkPolicy {
       return Def && Tracker.isFromThisPTU(Def, ID);
     };
 
-    /// Keyed by the canonical declaration: all redeclarations share a single
-    /// RedeclLink, so truncating more than once would advance past the intended
-    /// surviving declaration.
+    /// Use the canonical decl because redecl unlinking should happen once per
+    /// redecl chain, not once per individual decl. A PTU can contain multiple
+    /// redecls, so we perform the unlinking only once.
     bool DetachFromRedecl = !RepairedChains.count(Canon) &&
                             D->getPreviousDecl() &&
                             !Tracker.isFromThisPTU(Canon, ID);
@@ -716,8 +763,6 @@ class DeclStateUnlinkPolicy {
     if (needsDefDataDetach(D))
       Reverter.detachDefData(D);
 
-    // Keyed on the primary context, a different granularity: a reopened
-    // namespace's redeclarations all resolve to the same primary.
     if (const DeclContext *DC = D->getDeclContext()->getPrimaryContext();
         TouchedDC.contains(DC))
       Reverter.detachFromDCLookup(const_cast<Decl *>(D), ID);
@@ -736,7 +781,8 @@ class DeclStateUnlinkPolicy {
     }
 
     if (const DeclContext *LexicalDC = D->getLexicalDeclContext();
-        D->isImplicit() && !RepairedLexicalContexts.count(LexicalDC)) {
+        !Tracker.isFromThisPTU(LexicalDC, ID) &&
+        !RepairedLexicalContexts.count(LexicalDC)) {
       Reverter.repairLexicalChain(*const_cast<DeclContext *>(LexicalDC), ID);
       RepairedLexicalContexts.insert(LexicalDC);
     }
@@ -757,6 +803,22 @@ void PTUMutationActions::walkDecls(const DeclContext *DC,
 //////////////////////// PTUMutationActions::commit ///////////////////////
 /// -----------------------------------------------------------------------
 
+/// This are the places to commit or update the incremental state of a decl
+/// based on its DeclShape.
+///
+/// The idea is to narrow each decl shape down to a small set of known
+/// mutation sites that require their state to be registered with
+/// IncrementalStateTracker. The state is committed when the decl is newly
+/// created by this PTU, and updated when an existing decl is mutated.
+///
+/// Currently, known mutation sites include commitClass, commitFunction,
+/// commitEnum, commitTemplate, etc. This follows a DeclShape ->
+/// mutation type relationship.
+///
+/// When adding a new mutation site that requires committing or updating
+/// state, it should be added based on the corresponding DeclShape ->
+/// mutation type relationship.
+
 void PTUMutationActions::commitMembers(PTUID ID, const DeclContext *Members) {
   DeclStateCommitPolicy Policy(ID, *this);
   walkDecls(Members, Policy);
@@ -768,6 +830,7 @@ void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
   using ClassMutation = MutationType;
 
   if (Rec.has(ClassMutation::TypeForDecl)) {
+    // Require friend grant to TypeDecl.
     // for (const TagDecl *Redecl : RD->redecls()) {
     //   const auto *Last = Tracker.getFootprints().mostRecent<const Type *>(
     //       Redecl, MutationType::TypeForDecl);
@@ -784,7 +847,6 @@ void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
         RD, MutationType::DefinitionData, ID,
         DeclStateReverter::createDefinitionDataFootprint(*RD));
 
-    /// SpecializationDecl can have lazy implicit generated body;
     if (Rec.has(ClassMutation::DefinitionInstantiate)) {
       if (RD->getMemberSpecializationInfo())
         commitMembers(ID, cast<DeclContext>(RD));
@@ -797,33 +859,29 @@ void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
       Tracker.commitFootprint(
           Spec, MutationType::SpecInfo, ID,
           DeclStateReverter::createFootprint<SpecializationFootprint>(*Spec));
-      // Member declarations are instantiated eagerly with the class
-      // definition regardless of TSK; only their definitions defer. Walk
-      // them now so each member's MemberSpecializationInfo is tracked from
-      // the moment it exists.
+      /// Member declarations are instantiated eagerly with the class
+      /// definition, egardless of TSK; only their definitions are deferred.
+      ///
+      /// Walk them here so each member’s MemberSpecializationInfo is tracked as
+      /// soon as it is created.
       if (IsNew && Spec->getSpecializationKind() == TSK_ImplicitInstantiation)
         commitMembers(ID, cast<DeclContext>(Spec));
-      if (IsNew)
-        registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
-      else
-        settleIfClosed(Spec, DeclShape::Class,
-                       uint32_t(MutationType::SpecInfo));
+      // FIXME: Remove syncSweepTracking from all places
+      // (SpecInfo/MemberSpecInfo) once the missing notifier
+      // (ASTMutationListener) is added for (SpecInfo/MemberSpecInfo).
+      syncSweepTracking(Spec, DeclShape::Class,
+                        uint32_t(MutationType::SpecInfo), IsNew);
     }
   }
 
   if (Rec.has(ClassMutation::MemberSpecInfo)) {
-    // A nested member of a class template -- its MSI's kind or
-    // point-of-instantiation changed.
     if (RD->getMemberSpecializationInfo()) {
       Tracker.commitFootprint(
           RD, MutationType::MemberSpecInfo, ID,
           DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
               *RD));
-      if (IsNew)
-        registerLiveVerification(RD, uint32_t(MutationType::MemberSpecInfo));
-      else
-        settleIfClosed(RD, DeclShape::Class,
-                       uint32_t(MutationType::MemberSpecInfo));
+      syncSweepTracking(RD, DeclShape::Class,
+                        uint32_t(MutationType::MemberSpecInfo), IsNew);
     }
   }
 }
@@ -858,10 +916,8 @@ void PTUMutationActions::commitFunction(PTUID ID, const FunctionDecl *FD,
         FD, MutationType::SpecInfo, ID,
         DeclStateReverter::createFootprint<FunctionSpecializationFootprint>(
             *FD));
-    if (IsNew)
-      registerLiveVerification(FD, uint32_t(MutationType::SpecInfo));
-    else
-      settleIfClosed(FD, DeclShape::Function, uint32_t(MutationType::SpecInfo));
+    syncSweepTracking(FD, DeclShape::Function, uint32_t(MutationType::SpecInfo),
+                      IsNew);
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -870,11 +926,8 @@ void PTUMutationActions::commitFunction(PTUID ID, const FunctionDecl *FD,
           FD, MutationType::MemberSpecInfo, ID,
           DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
               *FD));
-      if (IsNew)
-        registerLiveVerification(FD, uint32_t(MutationType::MemberSpecInfo));
-      else
-        settleIfClosed(FD, DeclShape::Function,
-                       uint32_t(MutationType::MemberSpecInfo));
+      syncSweepTracking(FD, DeclShape::Function,
+                        uint32_t(MutationType::MemberSpecInfo), IsNew);
     }
   }
 }
@@ -882,28 +935,20 @@ void PTUMutationActions::commitFunction(PTUID ID, const FunctionDecl *FD,
 void PTUMutationActions::commitVar(PTUID ID, const VarDecl *VD,
                                    MutationRecord &Rec, bool IsNew) {
 
-  // EvaluatedStmt / APValue: populated on the FIRST constant-evaluation of
-  // this variable, which can land in any PTU long after the VarDecl was
-  // created. This is a mutation of an earlier PTU's decl, not a property
-  // settled at declaration time.
+  // EvaluatedStmt / APValue: set on the first constant evaluation of the
+  // variable, which can happen in a later PTU.
   if (Rec.has(MutationType::EvaluatedValue)) {
-    // No footprint chain -- write-once field with a fixed null prior
-    // state (see restoreVar), so nothing to seed a baseline for.
+    // No footprint: this is a write-once field with a fixed null
+    // initial state, so there is no baseline to track.
     if (IsNew) {
-      // Just created: register VD so a later commit's
-      // SweepTracker::sweep() call notices if/when WasEvaluated actually
-      // flips (no listener exists to tell us directly -- see
-      // hiddenKindsMask).
+      // Track the VarDecl so a later sweep can detect when WasEvaluated flips.
+      // There is no listener for this mutation.
       HiddenMutationTracker.track(VD, uint32_t(MutationType::EvaluatedValue));
     } else {
-      // This IS the confirmation, arriving here via SweepTracker::sweep()'s
-      // OnConfirmed callback (there is no other path to this bit -- no
-      // listener exists for it). Write-once means terminal: once
-      // WasEvaluated is true it can never become false again on its own,
-      // so settle the bit now rather than waiting for the next sweep to
-      // notice kindsNeedingTracking no longer flags it. restoreVar
-      // is what re-track()s it, only if a rollback actually reverts this.
-      HiddenMutationTracker.settle(VD, uint32_t(MutationType::EvaluatedValue));
+      // This is the confirmation from SweepTracker::sweep(). Since this is a
+      // write-once field, it is now settled and no longer needs tracking.
+      // restoreVar() will re-track it if a rollback reverts the mutation.
+      HiddenMutationTracker.untrack(VD, uint32_t(MutationType::EvaluatedValue));
     }
   }
 
@@ -911,15 +956,12 @@ void PTUMutationActions::commitVar(PTUID ID, const VarDecl *VD,
   //   }
 
   if (Rec.has(MutationType::SpecInfo)) {
-    // Unlike functions, a variable specialization IS a distinct type.
     const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
     Tracker.commitFootprint(
         Spec, MutationType::SpecInfo, ID,
         DeclStateReverter::createFootprint<VarSpecializationFootprint>(*Spec));
-    if (IsNew)
-      registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
-    else
-      settleIfClosed(Spec, DeclShape::Var, uint32_t(MutationType::SpecInfo));
+    syncSweepTracking(Spec, DeclShape::Var, uint32_t(MutationType::SpecInfo),
+                      IsNew);
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -928,11 +970,8 @@ void PTUMutationActions::commitVar(PTUID ID, const VarDecl *VD,
           VD, MutationType::MemberSpecInfo, ID,
           DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
               *VD));
-      if (IsNew)
-        registerLiveVerification(VD, uint32_t(MutationType::MemberSpecInfo));
-      else
-        settleIfClosed(VD, DeclShape::Var,
-                       uint32_t(MutationType::MemberSpecInfo));
+      syncSweepTracking(VD, DeclShape::Var,
+                        uint32_t(MutationType::MemberSpecInfo), IsNew);
     }
   }
 }
@@ -956,18 +995,13 @@ void PTUMutationActions::commitEnum(PTUID ID, const EnumDecl *ED,
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
-    // A scoped member enumeration of a class template -- instantiated with
-    // the enclosing specialization, carrying MSI back to the pattern enum.
     if (ED->getMemberSpecializationInfo()) {
       Tracker.commitFootprint(
           ED, MutationType::MemberSpecInfo, ID,
           DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
               *ED));
-      if (IsNew)
-        registerLiveVerification(ED, uint32_t(MutationType::MemberSpecInfo));
-      else
-        settleIfClosed(ED, DeclShape::Enum,
-                       uint32_t(MutationType::MemberSpecInfo));
+      syncSweepTracking(ED, DeclShape::Enum,
+                        uint32_t(MutationType::MemberSpecInfo), IsNew);
     }
   }
 }
@@ -981,8 +1015,8 @@ void PTUMutationActions::commitTemplate(PTUID ID,
       HiddenMutationTracker.track(RTCanon,
                                   uint32_t(MutationType::TemplateCommon));
     else
-      HiddenMutationTracker.settle(RTCanon,
-                                   uint32_t(MutationType::TemplateCommon));
+      HiddenMutationTracker.untrack(RTCanon,
+                                    uint32_t(MutationType::TemplateCommon));
   }
 
   if (Rec.has(MutationType::CanonInjectedTST)) {
@@ -990,8 +1024,8 @@ void PTUMutationActions::commitTemplate(PTUID ID,
       HiddenMutationTracker.track(RTCanon,
                                   uint32_t(MutationType::CanonInjectedTST));
     else
-      HiddenMutationTracker.settle(RTCanon,
-                                   uint32_t(MutationType::CanonInjectedTST));
+      HiddenMutationTracker.untrack(RTCanon,
+                                    uint32_t(MutationType::CanonInjectedTST));
   }
 }
 
@@ -1001,7 +1035,7 @@ void PTUMutationActions::commitTypedef(PTUID ID, const TypedefNameDecl *TD,
     if (IsNew)
       HiddenMutationTracker.track(TD, uint32_t(MutationType::TypeForDecl));
     else
-      HiddenMutationTracker.settle(TD, uint32_t(MutationType::TypeForDecl));
+      HiddenMutationTracker.untrack(TD, uint32_t(MutationType::TypeForDecl));
   }
 }
 
@@ -1024,20 +1058,34 @@ void PTUMutationActions::commit(TranslationUnitDecl *ThisTU) {
   assert(!Cur.Commited);
   PTUID ID = Cur.ID;
 
+  // 1. Verify any unconfirmed mutation (see : KindNeedingVerification) reported
+  // by the listener, whether true or false.
   Cur.verifyMutations(*this);
+  // 2. Catch any mutation-sensitive state of Decl from previous PTUs that
+  //    ASTMutationListener cannot catch if it was registered during a
+  //    previous PTU commit.
   Tracker.getHiddenMutationTracker().sweep(
       *this, [&](const Decl *D, DeclShape S, uint32_t K) {
         Cur.noteMutated(D, S, K);
       });
 
+  // 3. Update the tracking state of declarations from the previous PTU using
+  //    Cur.Mutations reported by ASTMutationListener and HiddenMutationTracker.
   for (auto &[D, Rec] : Cur.Mutations)
     commitDecl(ID, D, Rec, /*IsNew=*/false);
 
-  DeclStateCommitPolicy Proxy(ID, *this);
+  // 4. Cur.ImplicitDecls: Declarations created by this PTU. Register them for
+  //    tracking if they have mutation-sensitive state that can be modified by
+  //    later PTUs.
+  //
+  // DeclStateCommitPolicy: Decides what to do with declarations created by the
+  //    current PTU, such as whether a declaration needs to be tracked or not.
+  DeclStateCommitPolicy Policy(ID, *this);
   for (const Decl *D : Cur.ImplicitDecls)
-    Proxy.process(D);
+    Policy.process(D);
 
-  walkDecls(ThisTU, Proxy);
+  // Iterate over all declarations from this TU and apply DeclStateCommitPolicy.
+  walkDecls(ThisTU, Policy);
 
   Cur.Commited = true;
 }
@@ -1046,6 +1094,18 @@ void PTUMutationActions::commit(TranslationUnitDecl *ThisTU) {
 //////////////////////// PTUMutationActions::restore //////////////////////
 /// -----------------------------------------------------------------------
 
+/// These are the places where we restore or undo/unlink the incremental state
+/// of a decl based on its DeclShape.
+///
+/// The same DeclShape -> mutation type relationship used during commit is
+/// followed here, but in the opposite direction. Restore handles unlinking
+/// newly created decls and undoing the committed or updated state of existing
+/// decls, restoring them to their previous state.
+///
+/// As with commit, when adding a new mutation site that requires restoring or
+/// unlinking state, it should be added based on the corresponding
+/// DeclShape -> mutation type relationship.
+
 void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
                                       MutationRecord &Rec) {
 
@@ -1053,7 +1113,7 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
       Rec.has(MutationType::DefinitionData)) {
     if (Rec.has(MutationType::DefinitionInstantiate)) {
       DeclStateReverter::revertDefinitionArrival(
-          *const_cast<CXXRecordDecl *>(RD));
+          const_cast<CXXRecordDecl *>(RD));
     } else if (const auto *Restored =
                    Tracker.getFootprints().mostRecent<DefinitionDataFootprint>(
                        RD, MutationType::DefinitionData)) {
@@ -1062,7 +1122,6 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
     }
   }
 
-  // TypeForDecl is shared across the whole redeclaration chain -- revert all.
   if (Rec.has(MutationType::TypeForDecl)) {
     // for (const TagDecl *Redecl : RD->redecls()) {
     //   const auto *Last = Tracker.Footprints.mostRecent<const Type *>(
@@ -1082,7 +1141,7 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
                 Spec, MutationType::SpecInfo))
       DeclStateReverter::restoreFootprint<SpecializationFootprint>(
           *Restored, *const_cast<ClassTemplateSpecializationDecl *>(Spec));
-    registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
+    trackForSweep(Spec, uint32_t(MutationType::SpecInfo));
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -1094,7 +1153,7 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
                 RD, MutationType::MemberSpecInfo))
       DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
           *Restored, *const_cast<CXXRecordDecl *>(RD));
-    registerLiveVerification(RD, uint32_t(MutationType::MemberSpecInfo));
+    trackForSweep(RD, uint32_t(MutationType::MemberSpecInfo));
   }
 }
 
@@ -1117,7 +1176,7 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
   }
 
   if (Rec.has(MutationType::DefinitionInstantiate))
-    DeclStateReverter::revertDefinitionArrival(*const_cast<FunctionDecl *>(FD));
+    DeclStateReverter::revertDefinitionArrival(const_cast<FunctionDecl *>(FD));
 
   if (Rec.has(MutationType::SpecInfo)) {
     // A function template specialization is a plain FunctionDecl carrying
@@ -1129,7 +1188,8 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
                 FD, MutationType::SpecInfo))
       DeclStateReverter::restoreFootprint<FunctionSpecializationFootprint>(
           *Restored, *const_cast<FunctionDecl *>(FD));
-    registerLiveVerification(FD, uint32_t(MutationType::SpecInfo));
+    const_cast<FunctionDecl *>(FD)->setInstantiationIsPending(false);
+    trackForSweep(FD, uint32_t(MutationType::SpecInfo));
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -1139,7 +1199,8 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
                 FD, MutationType::MemberSpecInfo))
       DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
           *Restored, *const_cast<FunctionDecl *>(FD));
-    registerLiveVerification(FD, uint32_t(MutationType::MemberSpecInfo));
+    const_cast<FunctionDecl *>(FD)->setInstantiationIsPending(false);
+    trackForSweep(FD, uint32_t(MutationType::MemberSpecInfo));
   }
 }
 
@@ -1177,9 +1238,6 @@ void PTUMutationActions::restoreTemplate(PTUID ID,
 void PTUMutationActions::restoreTypedef(PTUID ID, const TypedefNameDecl *TD,
                                         MutationRecord &Rec) {
   if (Rec.has(MutationType::TypeForDecl)) {
-    // TD survives this rollback (only the cached Type was this PTU's
-    // doing) -- reset the live field and re-register: some later PTU
-    // could re-trigger ASTContext::getTypedefType's first-call write.
     // DeclStateReverter::resetTypeForDecl(const_cast<TypedefNameDecl *>(TD));
     // HiddenMutationTracker.track(TD, uint32_t(MutationType::TypeForDecl));
   }
@@ -1200,7 +1258,7 @@ void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
   }
 
   if (Rec.has(MutationType::DefinitionInstantiate)) {
-    DeclStateReverter::revertDefinitionArrival(*const_cast<VarDecl *>(VD));
+    DeclStateReverter::revertDefinitionArrival(const_cast<VarDecl *>(VD));
   }
 
   if (Rec.has(MutationType::SpecInfo)) {
@@ -1210,7 +1268,7 @@ void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
                 Spec, MutationType::SpecInfo))
       DeclStateReverter::restoreFootprint<VarSpecializationFootprint>(
           *Restored, *const_cast<VarTemplateSpecializationDecl *>(Spec));
-    registerLiveVerification(VD, uint32_t(MutationType::SpecInfo));
+    trackForSweep(VD, uint32_t(MutationType::SpecInfo));
   }
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -1220,7 +1278,7 @@ void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
                 VD, MutationType::MemberSpecInfo))
       DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
           *Restored, *const_cast<VarDecl *>(VD));
-    registerLiveVerification(VD, uint32_t(MutationType::MemberSpecInfo));
+    trackForSweep(VD, uint32_t(MutationType::MemberSpecInfo));
   }
 }
 
@@ -1236,9 +1294,8 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
     // }
   }
 
-  //   if (Rec.has(MutationType::DefinitionInstantiate))
-  //     DeclStateReverter::revertDefinitionArrival(*const_cast<EnumDecl
-  //     *>(ED));
+  if (Rec.has(MutationType::DefinitionInstantiate))
+    DeclStateReverter::revertDefinitionArrival(const_cast<EnumDecl *>(ED));
 
   if (Rec.has(MutationType::MemberSpecInfo)) {
     assert(ED->getMemberSpecializationInfo());
@@ -1248,7 +1305,7 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
       DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
           *Restored, *const_cast<EnumDecl *>(ED));
 
-    registerLiveVerification(ED, uint32_t(MutationType::MemberSpecInfo));
+    trackForSweep(ED, uint32_t(MutationType::MemberSpecInfo));
   }
 }
 
@@ -1273,13 +1330,13 @@ void PTUMutationActions::restoreSpecialMemberCache(PTUID ID) {
     return;
 
   Sema &SemaRef = Tracker.getSema();
-  llvm::SmallVector<Sema::SpecialMemberCacheKey, 8> Stale;
+  llvm::SmallVector<Sema::SpecialMemberCacheKey, 8> ToRemove;
   for (auto &Entry : SemaRef.SpecialMemberCache) {
     CXXMethodDecl *MD = Entry.second.getMethod();
     if (MD && (Tracker.isFromThisPTU(MD, ID) || Cur.ImplicitDecls.contains(MD)))
-      Stale.push_back(Entry.first);
+      ToRemove.push_back(Entry.first);
   }
-  for (const auto &Key : Stale)
+  for (const auto &Key : ToRemove)
     SemaRef.SpecialMemberCache.erase(Key);
 }
 
@@ -1288,10 +1345,27 @@ void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
   assert((!Cur.Commited || ThisTU == Cur.ThisPTU) &&
          "MostRecentTU doesn't match the TU recorded at commit time");
   PTUID ID = Cur.ID;
-  if (Cur.Commited)
+  // 1. Check whether this PTU was committed. If the PTU failed, it was not
+  //    committed and therefore did not go through the commit policy or register
+  //    any declaration state, so there is nothing to undo from
+  //    IncrementalStateTracker.
+  //
+  //    If the PTU was successfully committed, the commit policy must have
+  //    registered the declaration state and updated the state of declarations
+  //    from the previous PTU in IncrementalStateTracker. First undo all state
+  //    recorded by IncrementalStateTracker, then perform the regular undo so
+  //    that mutated declarations are restored to the PTU(N-1) state rather
+  //    than the current PTU state.
+  if (Cur.Commited) {
+    // remove sweep-tracked decls owned by this PTU,
+    // which would otherwise become stale references with no cleanup path.
+    Tracker.getHiddenMutationTracker().untrackIf(
+        [&](const Decl *D) { return Tracker.isFromThisPTU(D, ID); });
     Tracker.undoLastEntries();
-  else {
-    // TODO: verify Mutations;
+  } else {
+    // Same as the commit() step, but performed here because this PTU failed and
+    // was never committed. Since the commit step was never executed, we need to
+    // perform it here for an uncommitted PTU.
     Cur.verifyMutations(*this);
     Tracker.getHiddenMutationTracker().sweep(
         *this, [&](const Decl *D, DeclShape S, uint32_t K) {
@@ -1299,29 +1373,37 @@ void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
         });
   }
 
+  // 2. Same as the commit() step, but in reverse: commit() updates the
+  //    IncrementalStateTracker with the mutated state of declarations from the
+  //    previous state. Here, we restore each mutated declaration using its most
+  //    recent state in IncrementalStateTracker (i.e., the PTU(N-1) state).
   for (auto &[D, Rec] : Cur.Mutations)
     restoreDecl(ID, D, Rec);
 
   DeclStateReverter Detacher(Tracker.getPTUSlabCheckpoints());
+  // 3. Cur.ImplicitDecls: Declarations created by this PTU. Unlink them from
+  //    the compiler's shared state.
+  //
+  //    This is the counterpart to the commit DeclStateCommitPolicy, but
+  //    performs the opposite operation by unlinking the declarations.
+  //
+  // DeclStateUnlinkPolicy: Decides what to do with declarations created by the
+  //    current PTU, such as whether a declaration needs to be unlinked from
+  //    redeclarations, DeclContext lookup, etc.
   DeclStateUnlinkPolicy Policy(ID, Tracker, Detacher);
-
   for (const Decl *D : Cur.ImplicitDecls)
     Policy.process(D);
 
   walkDecls(ThisTU, Policy);
 
-  // SpecialMemberCache stores the CXXMethodDecl* resolved by a previous
-  // LookupSpecialMember() call and returns it directly on a cache hit.
-  // If that method was created by a PTU that is being rolled back, the entry
-  // is stale, so remove it here.
+  // SpecialMemberCache stores the CXXMethodDecl* from a previous
+  // LookupSpecialMember() call. If it was created by a PTU being rolled back,
+  // the cache entry is stale, so remove it here.
   restoreSpecialMemberCache(ID);
 
-  // Must be last: Cur/ID/ThisTU are all references into (or derived from)
-  // the PTU this call retires, and popCurrentPTU() destroys that entry.
-  // Runs for BOTH branches above (committed or not) -- a PTU that never
-  // committed still needs its checkpoint-ledger slot and PTUStack
-  // entry reclaimed, otherwise every failed input would permanently grow
-  // both, and NextID would desync from PTUSlabCheckpoints on top of that.
+  // Must be last: Cur/ID/ThisTU refer to data owned by this PTU, and
+  // popCurrentPTU() destroys that entry. This runs for both committed and
+  // rolled-back PTUs to reclaim the PTUStack and checkpoint entry.
   Tracker.popCurrentPTU();
 }
 
@@ -1329,6 +1411,33 @@ void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
 ////////////////////////// MutationListener ///////////////////////////////
 /// -----------------------------------------------------------------------
 
+/// This is the place for listener-related functionality. When Clang notifies
+/// us about an AST mutation, we record it in PTUStateInfo.
+///
+/// We only capture mutations here. For example, if a decl from a previous PTU
+/// is mutated, we record its Decl -> DeclShape -> mutation type relationship.
+/// We do not process the mutation here. Processing is deferred until commit
+/// if the PTU succeeds; if the PTU fails, the flow goes through restore
+/// instead.
+///
+/// We also track implicit decls. During commit, we walk the current PTU and
+/// can only commit decls that are visible from that TU. However, implicit decls
+/// can be created and added to another PTU, so we need to track them as well
+/// so they can be committed or unlinked during restore.
+///
+/// We do not cover every mutation case here yet. Any new case added should
+/// follow the same rule: if a mutation affects a decl from a previous PTU,
+/// record it in PTUStateInfo.
+///
+/// There are two separate restore paths:
+/// 1. Restoring a mutated decl to its previous state.
+/// 2. Unlinking a decl from shared state, such as redecl chains or DC lookup.
+///
+/// For example, see noteDefinitionInstantiated, where a decl is created by
+/// Clang and added to another PTU even though it is not marked as implicit.
+/// Such a decl needs to be tracked for unlinking rather than restoring its
+/// previous state.
+
 void PTUMutationRecorder::CompletedTagDefinition(const TagDecl *D) {
   if (!D->isDefinedOutsideFunctionOrMethod())
     return; // ordinary local class -- structurally unreachable by any other
@@ -1400,8 +1509,9 @@ void PTUMutationRecorder::noteTemplateDeclMutation(const TemplateT *TD,
   if (Spec->isImplicit())
     Cur.ImplicitDecls.insert(Spec);
 
-  if (Tracker.isFromThisPTU(TD, Cur.ID))
+  if (Tracker.isFromThisPTU(TD, Cur.ID)) {
     return;
+  }
 
   Cur.noteMutated(TD, DeclShape::Template, MutationType::SpecializationAdded);
 }
@@ -1441,6 +1551,14 @@ void PTUMutationRecorder::DeducedReturnType(const FunctionDecl *FD,
 void PTUMutationRecorder::noteDefinitionInstantiated(const Decl *D) {
   PTUStateInfo &Cur = Tracker.current();
   if (Tracker.isFromThisPTU(D, Cur.ID)) {
+    // Normally, walkDecls covers declarations created in this PTU.
+    // But an out-of-line static data member is a separate VarDecl whose
+    // lexical context is the class, so the namespace/TU walk won't find it.
+    // Process it like other implicit declarations instead.
+    const auto *VD = dyn_cast<VarDecl>(D);
+    if (VD && VD->getPreviousDecl() &&
+        !Tracker.isFromThisPTU(VD->getCanonicalDecl(), Cur.ID))
+      Cur.ImplicitDecls.insert(D);
     return;
   }
   Cur.noteMutated(D, classifyShape(D), MutationType::DefinitionInstantiate);
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index c03881b0ed336..78fe1399f0d0e 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -571,9 +571,7 @@ Interpreter::Parse(llvm::StringRef Code) {
   class PTURollbackGuard {
   public:
     explicit PTURollbackGuard(Sema &S)
-        : Ctx(S.getASTContext()),
-          CheckPoint(Ctx.getAllocator().checkPoint()) {
-          }
+        : Ctx(S.getASTContext()), CheckPoint(Ctx.getAllocator().CheckPoint()) {}
 
     ~PTURollbackGuard() {
       if (!Committed) {
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index 0aceaa999e2a6..56bce4d31c8a7 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -43,9 +43,9 @@ LLVM_ABI void printBumpPtrAllocatorStats(unsigned NumSlabs, size_t TotalMemory);
 } // end namespace detail
 
 struct SlabCheckPoint {
-  unsigned ActiveSlabIdx;
-  char *CurPtr;
-  uintptr_t EndSentinel;
+  unsigned SlabIdx;
+  const char *CurPtr;
+  unsigned CustomSlabCount;
 };
 
 /// Allocate memory in an ever growing pool, as if by bump-pointer.
@@ -216,16 +216,8 @@ class BumpPtrAllocatorImpl
       return AlignedPtr;
     }
 
-    if (!Slabs.empty() && Slabs.size() > 1 &&
-        (ActiveSlabIdx < Slabs.size() - 1)) {
-      ActiveSlabIdx++;
-      void *NewSlab = Slabs[ActiveSlabIdx];
-      size_t AllocatedSlabSize = computeSlabSize(ActiveSlabIdx);
-      CurPtr = (char *)(NewSlab);
-      EndSentinel = uintptr_t(NewSlab) + AllocatedSlabSize + 1;
-    } else
-      // Otherwise, start a new slab and try again.
-      StartNewSlab();
+    // Otherwise, start a new slab and try again.
+    StartNewSlab();
     uintptr_t AlignedAddr = alignAddr(CurPtr, Alignment);
     assert(AlignedAddr + SizeToAllocate < EndSentinel &&
            "Unable to allocate memory!");
@@ -257,64 +249,46 @@ class BumpPtrAllocatorImpl
 
   size_t GetNumSlabs() const { return Slabs.size() + CustomSizedSlabs.size(); }
 
-  SlabCheckPoint checkPoint() const {
-    return {ActiveSlabIdx, CurPtr, EndSentinel};
-  }
-
-  static void poisonMemory(void *Ptr, size_t Size) {
-#if LLVM_ADDRESS_SANITIZER_BUILD
-    __asan_poison_memory_region(Ptr, Size);
-#else
-    // In non-ASAN builds, overwrite with a known poison pattern
-    // so use-after-rewind crashes deterministically in debug builds
-// #ifndef NDEBUG
-    memset(Ptr, 0xCD, Size); // 0xCD = classic "dead memory" pattern
-// #endif
-#endif
+  SlabCheckPoint CheckPoint() const {
+    return {static_cast<unsigned int>(Slabs.empty() ? 0u : (Slabs.size() - 1)),
+            CurPtr, static_cast<unsigned int>(CustomSizedSlabs.size())};
   }
 
   bool isAfterCheckpoint(const void *Ptr, const SlabCheckPoint &CP) const {
     const char *P = static_cast<const char *>(Ptr);
 
-    // Check active slab — past the checkpoint CurPtr
-    if (CP.ActiveSlabIdx < Slabs.size()) {
-      const char *Start = static_cast<const char *>(Slabs[CP.ActiveSlabIdx]);
-      if (P >= CP.CurPtr && P < (Start + computeSlabSize(CP.ActiveSlabIdx)))
-        return true;
+    // Regular (shared, bump-allocated) slabs.
+    if (!CP.CurPtr) {
+      // Checkpoint predates this allocator's first regular slab -- every
+      // regular slab that exists now was allocated after it.
+      for (unsigned I = 0; I < Slabs.size(); ++I) {
+        const char *Start = static_cast<const char *>(Slabs[I]);
+        if (P >= Start && P < Start + computeSlabSize(I))
+          return true;
+      }
+    } else {
+      if (CP.SlabIdx < Slabs.size()) {
+        const char *Start = static_cast<const char *>(Slabs[CP.SlabIdx]);
+        if (P >= CP.CurPtr && P < Start + computeSlabSize(CP.SlabIdx))
+          return true;
+      }
+      for (unsigned I = CP.SlabIdx + 1; I < Slabs.size(); ++I) {
+        const char *Start = static_cast<const char *>(Slabs[I]);
+        if (P >= Start && P < (Start + computeSlabSize(I)))
+          return true;
+      }
     }
 
-    // Check slabs allocated entirely after checkpoint
-    for (unsigned I = CP.ActiveSlabIdx + 1; I < Slabs.size(); ++I) {
-      const char *Start = static_cast<const char *>(Slabs[I]);
-      const char *End = Start + computeSlabSize(I);
-      if (P >= Start && P < End)
+    for (unsigned I = CP.CustomSlabCount; I < CustomSizedSlabs.size(); ++I) {
+      const char *Start = static_cast<const char *>(CustomSizedSlabs[I].first);
+      size_t Size = CustomSizedSlabs[I].second;
+      if (P >= Start && P < Start + Size)
         return true;
     }
 
     return false;
   }
 
-  void restoreToCheckPoint(SlabCheckPoint CP) {
-    assert(CP.ActiveSlabIdx >= 0 && CP.ActiveSlabIdx < Slabs.size());
-    assert(CP.CurPtr >= (const char *)Slabs[CP.ActiveSlabIdx] &&
-           CP.EndSentinel == uintptr_t(Slabs[CP.ActiveSlabIdx]) +
-                      computeSlabSize(CP.ActiveSlabIdx) + 1);
-    ActiveSlabIdx = CP.ActiveSlabIdx;
-    CurPtr = CP.CurPtr;
-    EndSentinel = CP.EndSentinel;
-
-    uintptr_t EndRange =
-        uintptr_t(Slabs[CP.ActiveSlabIdx]) + computeSlabSize(CP.ActiveSlabIdx);
-
-    llvm::outs() << "Poisoned range = [" << (void *)CurPtr << ", " << (void *)(EndSentinel - 1) << "]\n";
-    llvm::outs() << "Poisoned End Size = [" << (void *)CurPtr << ", " << (void *)(CurPtr + ((EndSentinel - 1) - (uintptr_t)CurPtr)) << "]\n";
-    llvm::outs().flush();
-    poisonMemory((void *)CurPtr, (size_t)(EndRange - uintptr_t(CurPtr)));
-    for (unsigned I = ActiveSlabIdx + 1; I < Slabs.size(); ++I)
-      // Should we deallocate any extra slabs?
-      poisonMemory(Slabs[I], computeSlabSize(I));
-  }
-
   /// \return An index uniquely and reproducibly identifying
   /// an input pointer \p Ptr in the given allocator.
   /// The returned value is negative iff the object is inside a custom-size
@@ -398,8 +372,6 @@ class BumpPtrAllocatorImpl
   /// path condition also rejects a empty allocator with a 0-size allocation.
   uintptr_t EndSentinel = 0;
 
-  unsigned ActiveSlabIdx = 0;
-
   /// The slabs allocated so far.
   SmallVector<void *, 4> Slabs;
 
@@ -424,8 +396,7 @@ class BumpPtrAllocatorImpl
   /// Allocate a new slab and move the bump pointers over into the new
   /// slab, modifying CurPtr and EndSentinel.
   void StartNewSlab() {
-    ActiveSlabIdx = Slabs.size();
-    size_t AllocatedSlabSize = computeSlabSize(ActiveSlabIdx);
+    size_t AllocatedSlabSize = computeSlabSize(Slabs.size());
 
     void *NewSlab = this->getAllocator().Allocate(AllocatedSlabSize,
                                                   alignof(std::max_align_t));

>From 1416c7bed43bbeaa5495171a93ffb5a8aca1aba3 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 30 Sep 2026 15:16:30 +0530
Subject: [PATCH 12/13] Some more fixes

---
 .../include/clang/Interpreter/ErrorRecovery.h | 41 +++++-------
 clang/lib/Interpreter/ErrRecovery.cpp         | 63 +++++++------------
 clang/lib/Interpreter/IncrementalParser.cpp   |  4 --
 3 files changed, 40 insertions(+), 68 deletions(-)

diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 85f7ffef1e8a0..ee392b697af74 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -45,6 +45,14 @@ struct DefinitionDataFootprint {
   bool operator!=(const DefinitionDataFootprint &O) const {
     return !(*this == O);
   }
+
+  // void update(const CXXRecordDecl &Live) {
+  //   DeclStateReverter::createDefinitionDataFootprint(*this, Live);
+  // }
+
+  // void restore(CXXRecordDecl &Live) const {
+  //   DeclStateReverter::restoreDefinitionDataFootprint(*this, Live);
+  // }
 };
 
 // Stores the state of a class template specialization. Tracks its
@@ -185,10 +193,6 @@ class DeclStateReverter {
     using RedeclarableTemplateDecl::Common;
   };
 
-  static const void *rawCommonPtr(const RedeclarableTemplateDecl &RT) {
-    return static_cast<const TemplateCommonAccess &>(RT).Common;
-  }
-
   // Common is `mutable`, so this is legal to call through a const
   // reference too -- no const_cast needed at any call site.
   static void clearCommonPtr(const RedeclarableTemplateDecl &RT) {
@@ -209,12 +213,6 @@ class DeclStateReverter {
     using VarTemplateDecl::getSpecializations;
   };
 
-  static bool canonInjectedTSTValid(const ClassTemplateDecl &CTD) {
-    auto *Ptr =
-        static_cast<const ClassTemplateCommonAccess &>(CTD).getCommonPtr();
-    return !Ptr->CanonInjectedTST.isNull();
-  }
-
   static void clearCanonInjectedTST(ClassTemplateDecl &CTD) {
     auto *Ptr = static_cast<ClassTemplateCommonAccess &>(CTD).getCommonPtr();
     Ptr->CanonInjectedTST = CanQualType();
@@ -266,9 +264,9 @@ class DeclStateReverter {
   static void restoreDefinitionDataFootprint(const DefinitionDataFootprint &FP,
                                              CXXRecordDecl &RD);
 
-  // Generic across every other footprint type -- SpecializationFootprint,
-  // VarSpecializationFootprint, FunctionSpecializationFootprint,
-  // MemberSpecializationFootprint.
+  // Generic across every other footprint type exclusing DefinitionDataFootprint
+  // -- SpecializationFootprint, VarSpecializationFootprint,
+  // FunctionSpecializationFootprint, MemberSpecializationFootprint.
   template <typename FootprintT, typename OwnerT>
   static FootprintT createFootprint(const OwnerT &Owner) {
     FootprintT FP;
@@ -284,18 +282,19 @@ class DeclStateReverter {
 
   // True if Common could still be created later -- i.e. nobody has called
   // getCommonPtr() anywhere in this template's redecl chain yet.
-  static bool isCommonPtrValid(const RedeclarableTemplateDecl &RT) {
-    return !rawCommonPtr(RT);
+  static bool isCommonPtrValid(const RedeclarableTemplateDecl *RT) {
+    return static_cast<const TemplateCommonAccess *>(RT)->Common != nullptr;
   }
 
   // True if CanonInjectedTST could still be cached later. Only valid to
   // call once Common itself is confirmed to exist (see needToTrackCommonPtr).
   static bool isTemplateCanonInjectedTSTValid(const ClassTemplateDecl *CTD) {
-    return !canonInjectedTSTValid(*CTD);
+    auto *Ptr =
+        static_cast<const ClassTemplateCommonAccess *>(CTD)->getCommonPtr();
+    return !Ptr->CanonInjectedTST.isNull();
   }
 
-  // Write-once fields, fixed null prior state (Common/CanonInjectedTST
-  // never existed before whichever PTU created them), so revert is a
+  // Write-once fields, fixed null prior state, so revert is a
   // direct reset, not a snapshot restore.
   static void resetTemplateCommonBase(RedeclarableTemplateDecl &RT) {
     clearCommonPtr(RT);
@@ -349,8 +348,6 @@ class DeclStateReverter {
 
   static void removeFromIdResolver(Sema &S, NamedDecl *D);
 
-  // Walk D's redecl chain looking for the newest decl that predates this
-  // PTU. Returns nullptr if the entire chain was created this PTU.
   template <typename DeclT> DeclT *findSurvivor(DeclT *D, PTUID ID) const;
 
   template <typename decl_type>
@@ -358,16 +355,12 @@ class DeclStateReverter {
     setLatestRedecl<decl_type>(*D->getFirstDecl(), Survivor);
   }
 
-  /// Point the canonical decl's "most recent" link back at the newest
-  /// redeclaration that predates this PTU.
   void detachFromRedeclChain(const Decl *D, PTUID ID);
 
   void repairLexicalChain(DeclContext &DC, PTUID ID);
 
 private:
   NamedDecl *tryDetachRedeclChain(Decl *D, PTUID ID);
-
-  void removeFromLookupMap(NamedDecl *ND, DeclContext *DC, PTUID ID);
 };
 
 /// Maps addresses to the PTU that allocated them, by keeping the slab
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index fc9182f16aaa0..af88ca6725334 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -42,16 +42,6 @@ DeclStateReverter::createDefinitionDataFootprint(const CXXRecordDecl &RD) {
   return FP;
 }
 
-// bool DeclStateReverter::compareDefinitionDataFootprint(
-//     const DefinitionDataFootprint &FP, const CXXRecordDecl &RD) {
-//   const auto &Live = RD.data();
-// #define FIELD(Name, Width, Merge) \
-//   if (FP.Name != Live.Name) \
-//     return false;
-// #include "clang/AST/CXXRecordDeclDefinitionBits.def"
-//   return true;
-// }
-
 void DeclStateReverter::restoreDefinitionDataFootprint(
     const DefinitionDataFootprint &FP, CXXRecordDecl &RD) {
   auto &Live = RD.data();
@@ -156,9 +146,6 @@ void DeclStateReverter::detachCommonBase(const RedeclarableTemplateDecl *RT) {
     clearCommonPtr(*R);
 }
 
-void DeclStateReverter::removeFromLookupMap(NamedDecl *ND, DeclContext *DC,
-                                            PTUID ID) {}
-
 void DeclStateReverter::detachFromExternCLookup(Decl *D,
                                                 DeclContext *ExternCCtx,
                                                 PTUID ID) {
@@ -166,47 +153,46 @@ void DeclStateReverter::detachFromExternCLookup(Decl *D,
   if (!ND)
     return;
 
-  bool IsExternC = false;
-  if (const auto *FD = dyn_cast<FunctionDecl>(ND))
-    IsExternC = FD->isExternC();
-  else if (const auto *VD = dyn_cast<VarDecl>(ND))
-    IsExternC = VD->isExternC();
+  // bool IsExternC = false;
+  // if (const auto *FD = dyn_cast<FunctionDecl>(ND))
+  //   IsExternC = FD->isExternC();
+  // else if (const auto *VD = dyn_cast<VarDecl>(ND))
+  //   IsExternC = VD->isExternC();
+
+  // if (IsExternC)
+  //   removeFromLookupMap(ND, ExternCCtx, ID);
 
-  if (IsExternC)
-    removeFromLookupMap(ND, ExternCCtx, ID);
+  if (StoredDeclsMap *Map = ExternCCtx->getPrimaryContext()->getLookupPtr()) {
+    auto It = Map->find(ND->getDeclName());
+    if (It != Map->end())
+      It->second.remove(ND);
+  }
 }
 
 void DeclStateReverter::removeFromIdResolver(Sema &S, NamedDecl *D) {
+  if (D->getDeclName().isEmpty())
+    return;
   if (D->getDeclName().getFETokenInfo())
     S.IdResolver.RemoveDecl(D);
 }
 
-/// Remove D from every lookup map it became visible in, reinstating the
-/// previous declaration where D had replaced one in-place (which is what
-/// StoredDeclsList::HandleRedeclaration does on a redeclaration --
-/// erasing the slot outright would lose the older decl entirely; that is
-/// the ReopenNs failure).
+/// Remove D from its DeclContext lookup map and restore the previous
+/// declaration if it was replaced.
 void DeclStateReverter::detachFromDCLookup(Decl *D, PTUID ID) {
   NamedDecl *ND = dyn_cast<NamedDecl>(D);
   if (!ND)
-    return; // nothing nameable -- never entered a lookup map
+    return;
 
-  // Chain must still be intact to find the replacement -- that is why
-  // this runs before detachFromRedeclChain.
+  // Find any declaration from the previous PTU in the redecl chain that may
+  // have been replaced by this declaration.
   NamedDecl *Survivor = findSurvivor(ND, ID);
   DeclarationName Name = ND->getDeclName();
 
-  // A decl in a transparent context (an unnamed namespace, an unscoped
-  // enum, a linkage-spec block) is visible in the enclosing context's map
-  // as well, so every level it was inserted into needs the same repair --
-  // not just its own primary context.
   DeclContext *DC = ND->getDeclContext();
   do {
     DeclContext *Primary = DC->getPrimaryContext();
     if (StoredDeclsMap *Map = Primary->getLookupPtr()) {
       auto Pos = Map->find(Name);
-      // Not an error: the map is built lazily, so a context that was
-      // never looked up in has no entry for this name at all.
       if (Pos != Map->end()) {
         StoredDeclsList &List = Pos->second;
         List.remove(ND);
@@ -219,7 +205,7 @@ void DeclStateReverter::detachFromDCLookup(Decl *D, PTUID ID) {
   } while (DC->isTransparentContext() && (DC = DC->getParent()));
 }
 
-// Walk D's redecl chain looking for the newest decl that predates this
+// Walk D's redecl chain looking for the newest decl that is from previous
 // PTU. Returns nullptr if the entire chain was created this PTU.
 template <typename DeclT>
 DeclT *DeclStateReverter::findSurvivor(DeclT *D, PTUID ID) const {
@@ -421,15 +407,12 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
     // already tracked
     const RedeclarableTemplateDecl *RT =
         cast<RedeclarableTemplateDecl>(D->getCanonicalDecl());
-    if (!DeclStateReverter::isCommonPtrValid(*RT) &&
+    if (!DeclStateReverter::isCommonPtrValid(RT) &&
         !HiddenMutationTracker.isTrackedFor(
             RT, uint32_t(MutationType::TemplateCommon))) {
       // No listener reports these Common changes, so use hidden tracking.
       Kinds |= uint32_t(MutationType::TemplateCommon);
     } else if (const auto *CTD = dyn_cast<ClassTemplateDecl>(RT)) {
-      // Common exists, so calling getCommonPtr() here is safe (won't
-      // allocate) -- only ClassTemplateDecl has CanonInjectedTST in its
-      // own Common, not the Function/Var template siblings.
       if (!DeclStateReverter::isTemplateCanonInjectedTSTValid(CTD))
         Kinds |= uint32_t(MutationType::CanonInjectedTST);
     }
@@ -567,7 +550,7 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
         Tracker.getHiddenMutationTracker().isTrackedFor(
             RT, uint32_t(MutationType::TemplateCommon))) {
       // No chain to compare against -- write-once.
-      if (DeclStateReverter::isCommonPtrValid(*RT))
+      if (DeclStateReverter::isCommonPtrValid(RT))
         Verified |= uint32_t(MutationType::TemplateCommon);
     }
     if (FlaggedKinds & uint32_t(MutationType::CanonInjectedTST) &&
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index 2c5a4d8489dc1..3cfabb5e5daa1 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -17,12 +17,8 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclContextInternals.h"
-<<<<<<< HEAD
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclVisitor.h"
-=======
-#include "clang/CodeGen/ModuleBuilder.h"
->>>>>>> 6d90b8675095 (Add Initial impl ErrorRecovery.h)
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"
 #include "clang/Parse/Parser.h"

>From 3162564c2861ef8759089a7c4baf239da999862c Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Thu, 1 Oct 2026 14:00:11 +0530
Subject: [PATCH 13/13] add missing header

---
 .../include/clang/Interpreter/ErrorRecovery.h |  6 +--
 clang/lib/Interpreter/ErrRecovery.cpp         |  4 +-
 llvm/include/llvm/Support/Allocator.h         | 38 +++++++------------
 3 files changed, 19 insertions(+), 29 deletions(-)

diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index ee392b697af74..09edb0798b1ca 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -19,6 +19,8 @@
 #include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
 #include "llvm/ADT/BitVector.h"
 #include <variant>
 
@@ -193,8 +195,6 @@ class DeclStateReverter {
     using RedeclarableTemplateDecl::Common;
   };
 
-  // Common is `mutable`, so this is legal to call through a const
-  // reference too -- no const_cast needed at any call site.
   static void clearCommonPtr(const RedeclarableTemplateDecl &RT) {
     static_cast<const TemplateCommonAccess &>(RT).Common = nullptr;
   }
@@ -287,7 +287,7 @@ class DeclStateReverter {
   }
 
   // True if CanonInjectedTST could still be cached later. Only valid to
-  // call once Common itself is confirmed to exist (see needToTrackCommonPtr).
+  // call once Common itself is confirmed to exist.
   static bool isTemplateCanonInjectedTSTValid(const ClassTemplateDecl *CTD) {
     auto *Ptr =
         static_cast<const ClassTemplateCommonAccess *>(CTD)->getCommonPtr();
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index af88ca6725334..4f6b6e1798cab 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -12,11 +12,13 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/Interpreter/ErrorRecovery.h"
+
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclContextInternals.h"
-#include "clang/Interpreter/ErrorRecovery.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/Sema/Sema.h"
 
 namespace clang {
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index 56bce4d31c8a7..832e6b1d16c30 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -250,39 +250,27 @@ class BumpPtrAllocatorImpl
   size_t GetNumSlabs() const { return Slabs.size() + CustomSizedSlabs.size(); }
 
   SlabCheckPoint CheckPoint() const {
-    return {static_cast<unsigned int>(Slabs.empty() ? 0u : (Slabs.size() - 1)),
+    return {Slabs.empty() ? 0u : static_cast<unsigned int>(Slabs.size() - 1),
             CurPtr, static_cast<unsigned int>(CustomSizedSlabs.size())};
   }
 
   bool isAfterCheckpoint(const void *Ptr, const SlabCheckPoint &CP) const {
     const char *P = static_cast<const char *>(Ptr);
 
-    // Regular (shared, bump-allocated) slabs.
-    if (!CP.CurPtr) {
-      // Checkpoint predates this allocator's first regular slab -- every
-      // regular slab that exists now was allocated after it.
-      for (unsigned I = 0; I < Slabs.size(); ++I) {
-        const char *Start = static_cast<const char *>(Slabs[I]);
-        if (P >= Start && P < Start + computeSlabSize(I))
-          return true;
-      }
-    } else {
-      if (CP.SlabIdx < Slabs.size()) {
-        const char *Start = static_cast<const char *>(Slabs[CP.SlabIdx]);
-        if (P >= CP.CurPtr && P < Start + computeSlabSize(CP.SlabIdx))
-          return true;
-      }
-      for (unsigned I = CP.SlabIdx + 1; I < Slabs.size(); ++I) {
-        const char *Start = static_cast<const char *>(Slabs[I]);
-        if (P >= Start && P < (Start + computeSlabSize(I)))
-          return true;
-      }
+    // From the checkpoint's slab onward, memory after CurPtr is new.
+    // All later slabs are new. If CurPtr is null, slab 0 is all new.
+    for (unsigned I = CP.SlabIdx, E = Slabs.size(); I < E; ++I) {
+      const char *S = static_cast<const char *>(Slabs[I]);
+      const char *Lo = (I == CP.SlabIdx && CP.CurPtr) ? CP.CurPtr : S;
+      if (P >= Lo && P < S + computeSlabSize(I))
+        return true;
     }
 
-    for (unsigned I = CP.CustomSlabCount; I < CustomSizedSlabs.size(); ++I) {
-      const char *Start = static_cast<const char *>(CustomSizedSlabs[I].first);
-      size_t Size = CustomSizedSlabs[I].second;
-      if (P >= Start && P < Start + Size)
+    // Custom-sized slabs are allocated whole, so the index alone decides.
+    for (unsigned I = CP.CustomSlabCount, E = CustomSizedSlabs.size(); I < E;
+         ++I) {
+      const char *S = static_cast<const char *>(CustomSizedSlabs[I].first);
+      if (P >= S && P < S + CustomSizedSlabs[I].second)
         return true;
     }
 



More information about the llvm-commits mailing list