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

via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 13 22:04:00 PDT 2026


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

## Cross-PTU Mutation Tracking for clang-repl Error Recovery

### The problem

clang-repl compiles each line/snippet you type as its own Partial Translation Unit (PTU). If a PTU fails partway through, we want to roll the AST back to exactly how it looked before that PTU started — so a failed input doesn't leave corrupted state behind for the next thing you type.

Rolling back new decls a failed PTU created is the easy part — just detach them. The hard part: parsing/Sema can mutate decls that already existed from earlier PTUs. For example:

- PTU1 defines `template<typename T> struct Foo;`.
- PTU2 does `Foo<int> f;` — this causes clang to lazily allocate internal bookkeeping on Foo's decl (its "Common" data) and cache a type for it. Foo's decl object, which has existed since PTU1, just got mutated by PTU2.
- If PTU2 fails and rolls back, we need to undo that mutation on Foo too — even though Foo itself belongs to PTU1 and must survive.

So the real problem is: for every decl, know which PTU is responsible for each piece of its current state, and be able to unwind just one PTU's contributions without disturbing anything from PTUs before or after it.

 ### The mental model

Every decl gets classified into a shape — Class, Function, Var, Enum, Template, Typedef — based on what kind of decl it is. Each shape has a fixed, known list of ways it can mutate (a bitmask of kinds — e.g. Class can get its definition completed, its cached type set, a specialization added; Var can get its constant value cached).

the system keeps two completely separate mechanism for anything that happens to a decl during a PTU:
- Created (just walks THIS PTU decls) — decls that didn't exist before this PTU; they were just born.
- Mutated — decls that already existed (from some earlier PTU) and got changed.

For each decl, we ask two questions, kept deliberately separate:

- "What could still change?" (`classifyPossibleKinds`) — re-evaluated fresh every time; a bit stops being flagged once that part of the decl is permanently settled.
- "What actually did change, and did this specific PTU cause it?" (`verifyMutationFor`) — the real confirmation, backed by evidence.

That evidence comes from two different sources, because clang itself gives us two different kinds of signal:

1. Listener-backed mutations. clang already fires callbacks (`ASTMutationListener`) for a lot of this — a class's definition completing, a template specialization being added, etc. When one fires, we snapshot the decl's relevant state into a small per-PTU history (a chain), tagged with the PTU's ID. This gives us a real "before" value to restore to.

2. Hidden mutations. Some things clang mutates with no listener at all — e.g. a template's internal "Common" block being lazily created on first use, or a variable's constant-evaluated value being cached the first time it's needed. Nothing tells us when these happen. For these, we register the decl in a `SweepTracker` at creation time, and at commit time we poll: "is the thing I'm watching for now true?" If yes, that's the confirmation, attributed to whoever's committing right now.

### Walking through it

- PTU1: creates `template <typename T> struct Foo;`. Recorded as a new decl, nothing to track yet.
- PTU2: uses `Foo<int>`, causing Foo's "Common" block to be lazily allocated. This is a hidden mutation — no listener fires. Because `Foo` was already registered as trackable, the SweepTracker notices at PTU2's commit: "Common just came into existence" → recorded as PTU2's mutation on Foo.
- PTU3: fails to compile for an unrelated reason.
  - Rollback only needs to touch what PTU3 itself did — `Foo` (owned by `PTU1`, mutated by `PTU2`) is untouched.
- Now suppose `PTU2` itself is later undone (e.g. you explicitly retract it): we look up what PTU2's own record says it did to `Foo` — "created Common" — and reverse exactly that: null the Common pointer back out, and re-open the tracking so a future PTU could legitimately recreate it. Foo itself survives untouched, since it belongs to `PTU1`.

### Commit vs. rollback, in short

- `commit()`: for every decl touched this PTU, record a snapshot into its history chain (listener-backed kinds) or confirm+settle its hidden-kind tracking (`SweepTracker`). This is what makes "undo this PTU later" possible at all.
- `restore()` (rollback of the current, in-progress PTU): for every decl this PTU's own records say it touched, reset just that piece of state back to what it was before, and detach any decl that was created entirely within this PTU.
- `undoLastEntries()` (rolling back a PTU that was already fully committed, e.g. an explicit "undo" of prior input): pops this PTU's own tail entries off every history chain it appended to, and releases any per-decl bookkeeping structure that was created entirely within this PTU back to its pool. The actual AST-field reset is still restore()'s job — this only unwinds the history bookkeeping that commit() built up, which committing alone doesn't touch.

>From c7f32e25380d8f14972ab422373869d8d5b0c05f Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 26 Jul 2026 12:29:25 +0530
Subject: [PATCH 1/3] 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   |   24 +
 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, 2732 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 5bb132f03db716..58acd333681592 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};
@@ -601,13 +612,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,
@@ -1355,6 +1371,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 eafeeecac77946..ea3c0e1b487c6f 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 9d233be282dbb8..06490fe69b984e 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 d87d8e8a663fa5..4cfd390a0ae83d 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 00000000000000..6d5605a9624e65
--- /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 c878e139fe70d0..843f20f82e3bcc 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 fc8da0ed560054..bb287180095fad 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 ad80074723817f..7d72e008d83dc7 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5541,6 +5541,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 70f61fa57a682a..28393566df8626 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -2203,6 +2203,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 00000000000000..18ad727ddc5ef4
--- /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 01d3295d1ac30d..ab8e418e8d7311 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 12beb542572d73..ff00264747c3fa 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -191,6 +191,28 @@ void IncrementalParser::withdrawMostRecentTU(
   C.TUDecl = Prev;
 }
 
+template <typename decl_type>
+void IncrementalParser::RepairRedeclChain(decl_type *D,
+                                          TranslationUnitDecl *PTU) {
+  decl_type *NewLatestDecl = nullptr;
+  decl_type *It = D->getMostRecentDecl();
+  while (It) {
+    if (It->getTranslationUnitDecl() != PTU) {
+      NewLatestDecl = It;
+      break;
+    }
+    if (It == It->getFirstDecl())
+      break;
+    It = It->getPreviousDecl();
+  }
+
+  if (!NewLatestDecl)
+    return; // entire chain from FailedTU
+
+  Redeclarable<decl_type> *RD = D->getFirstDecl();
+  RD->RedeclLink.setLatest(NewLatestDecl);
+}
+
 void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
   if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) {
     // Collect the keys to erase: erasing during iteration invalidates the map
@@ -256,6 +278,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 b626cebaafcd70..e6eaa7dddb7fdb 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 092f3ede771f65..0d6442dffd744f 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"
@@ -553,6 +554,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) {
@@ -576,6 +610,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();
@@ -588,6 +624,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 00000000000000..8dce1182c42794
--- /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 bb0ca118e20157..1a44e3eb9fb14b 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 bef7db56b70454c97c76702db39f9a6f31eceea0 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 26 Jul 2026 12:37:31 +0530
Subject: [PATCH 2/3] 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 06490fe69b984e..095490eeebd090 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 9af17df229b376..73ecb90fbcd883 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 6d5605a9624e65..245d9416a8933e 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 18ad727ddc5ef4..e25c67c46aa01a 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 85f00c36dd5aaa..3ca20d8584914a 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*/,
@@ -96,8 +96,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
@@ -114,8 +116,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 2893ff7b5baa6d..13461dee7500f5 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 ff00264747c3fa..24eb73904242df 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -85,6 +85,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 0d6442dffd744f..c009bfc55a9fc3 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -613,8 +613,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 8dce1182c42794..e72d0b7580bcbd 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 00000000000000..4a86aea81cf5b8
--- /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 00000000000000..207ae20d26683d
--- /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 00000000000000..5befdd7be33e5f
--- /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 00000000000000..9c3317e7171853
--- /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 00000000000000..f38b1c5fb97318
--- /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 6d90b8675095de34261f8352044778709c2944bd Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 13 Sep 2026 14:34:46 +0530
Subject: [PATCH 3/3] 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             |    4 +
 .../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   |   44 +-
 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, 1928 insertions(+), 382 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 58acd333681592..f1d1b0a9c941f3 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};
@@ -612,18 +603,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 7540b7f90ee868..14cb3b1d146124 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 ea3c0e1b487c6f..1cd51b32af64ea 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 095490eeebd090..c8a6534d770efb 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 afe46fae1bceb1..e7078333fa384c 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;
@@ -257,11 +259,13 @@ class CXXBaseSpecifier {
 /// Represents a C++ struct/union/class.
 class CXXRecordDecl : public RecordDecl {
   friend class ASTDeclMerger;
+  friend class ASTDeclDetacher;
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
   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 4cfd390a0ae83d..53732655bbafca 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 0d110bc3e63063..d2ec2e9807ae1f 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 5121b561288c03..dd490c688de184 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 73ecb90fbcd883..a7964b69feb033 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 35911ee2f7d163..8e5d42b301611f 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:
 //
@@ -188,6 +190,8 @@ class Redeclarable {
   friend class ASTDeclMerger;
   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 245d9416a8933e..0c47e9bc2296bd 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 bb287180095fad..97d2d22bb3ae9b 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 7d72e008d83dc7..c5be49f9ef1bb6 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5516,6 +5516,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));
 }
 
@@ -5542,10 +5546,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 28393566df8626..70f61fa57a682a 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -2203,9 +2203,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 4d27b04248f931..bd9f4af9b7fd20 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 e25c67c46aa01a..d9da3f7448da82 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 3ca20d8584914a..85f00c36dd5aaa 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*/,
@@ -96,10 +96,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
@@ -116,10 +114,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 13461dee7500f5..c3e4f108ea3659 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 24eb73904242df..8bb25c4fbc7772 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclContextInternals.h"
+#include "clang/CodeGen/ModuleBuilder.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"
 #include "clang/Parse/Parser.h"
@@ -85,9 +86,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.",
@@ -192,28 +192,6 @@ void IncrementalParser::withdrawMostRecentTU(
   C.TUDecl = Prev;
 }
 
-template <typename decl_type>
-void IncrementalParser::RepairRedeclChain(decl_type *D,
-                                          TranslationUnitDecl *PTU) {
-  decl_type *NewLatestDecl = nullptr;
-  decl_type *It = D->getMostRecentDecl();
-  while (It) {
-    if (It->getTranslationUnitDecl() != PTU) {
-      NewLatestDecl = It;
-      break;
-    }
-    if (It == It->getFirstDecl())
-      break;
-    It = It->getPreviousDecl();
-  }
-
-  if (!NewLatestDecl)
-    return; // entire chain from FailedTU
-
-  Redeclarable<decl_type> *RD = D->getFirstDecl();
-  RD->RedeclLink.setLatest(NewLatestDecl);
-}
-
 void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
   if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) {
     // Collect the keys to erase: erasing during iteration invalidates the map
@@ -270,6 +248,20 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
     }
   }
 
+  // llvm::SmallVector<llvm::StringRef> Decls;
+  // Decls.reserve(64);
+  // auto *Gen = Act->getCodeGen();
+  // for (auto &F : Gen->GetModule()->functions()) {
+  //   if (const Decl *D = Gen->GetDeclForMangledName(F.getName())) {
+  //     if (D->getTranslationUnitDecl() == MostRecentTU)
+  //       Decls.push_back(F.getName());
+  //   }
+  // }
+
+  // Act->getCodeGen()->restoreManglings(Decls);
+  // Act->getCodeGen()->restoreManglings();
+
+  // FIXME: We should de-allocate MostRecentTU
   for (Decl *D : MostRecentTU->decls()) {
     auto *ND = dyn_cast<NamedDecl>(D);
     if (!ND || ND->getDeclName().isEmpty())
@@ -279,8 +271,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 c009bfc55a9fc3..413726c746c4c9 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -579,8 +579,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;
@@ -610,11 +610,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();
   }
 
@@ -626,7 +626,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 e72d0b7580bcbd..1c24b634b94430 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 f38b1c5fb97318..48f3f0835af31d 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 b611e015e84d77..75f654dac058e3 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 1a44e3eb9fb14b..cc9bd67d62e903 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));



More information about the cfe-commits mailing list