[clang] [llvm] [Clang-Repl] Introduce Error Recovery for Clang-Repl. (PR #223305)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 1 01:34:24 PDT 2026
https://github.com/SahilPatidar updated https://github.com/llvm/llvm-project/pull/223305
>From 140cbe5c564e4e9e4645ddda72f6c9fa8b65eddc Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 26 Jul 2026 12:29:25 +0530
Subject: [PATCH 01/13] WIP: implement rollback (Error-Recovery) mechanism for
clang-repl
---
clang/include/clang/AST/ASTContext.h | 22 +-
clang/include/clang/AST/Decl.h | 3 +
clang/include/clang/AST/DeclBase.h | 6 +
.../include/clang/AST/DeclContextInternals.h | 3 +
.../include/clang/Interpreter/ErrorRecovery.h | 300 ++++
.../Interpreter/PartialTranslationUnit.h | 3 +
clang/include/clang/Sema/Sema.h | 2 +
clang/lib/AST/ASTContext.cpp | 4 +
clang/lib/AST/DeclBase.cpp | 3 +
.../lib/Interpreter/ASTContextStateStash.cpp | 1362 +++++++++++++++++
clang/lib/Interpreter/CMakeLists.txt | 2 +
clang/lib/Interpreter/IncrementalParser.cpp | 2 +
clang/lib/Interpreter/IncrementalParser.h | 3 +
clang/lib/Interpreter/Interpreter.cpp | 37 +
clang/lib/Interpreter/SemaStateStash.cpp | 882 +++++++++++
llvm/include/llvm/Support/Allocator.h | 80 +-
16 files changed, 2710 insertions(+), 4 deletions(-)
create mode 100644 clang/include/clang/Interpreter/ErrorRecovery.h
create mode 100644 clang/lib/Interpreter/ASTContextStateStash.cpp
create mode 100644 clang/lib/Interpreter/SemaStateStash.cpp
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 72e6bcf362aeb..01f83b4d85113 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -85,6 +85,7 @@ template <> struct DenseMapInfo<ScalableVecTyKey> {
namespace clang {
class APValue;
+class ASTContextStateStash;
class ASTMutationListener;
class ASTRecordLayout;
class AtomicExpr;
@@ -234,12 +235,22 @@ struct QualTypeBoolInfo {
}
};
+struct TypeForDeclMutation {
+ TypeDecl *Decl;
+ const Type *OldValue;
+};
+
/// Holds long-lived AST nodes (such as types and decls) that can be
/// referred to throughout the semantic analysis of a file.
class ASTContext : public RefCountedBase<ASTContext> {
friend class NestedNameSpecifier;
mutable SmallVector<Type *, 0> Types;
+
+ bool IncrementalErrorRecoveryMode = true;
+ mutable SmallVector<TypeForDeclMutation> PendingTypeForDeclMutations;
+ mutable llvm::DenseSet<const DeclContext *> PendingDCMutations;
+
mutable llvm::FoldingSet<ExtQuals> ExtQualNodes;
mutable llvm::UniquingSet<ComplexType> ComplexTypes;
mutable llvm::UniquingSet<PointerType> PointerTypes{GeneralTypesLog2InitSize};
@@ -605,13 +616,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
using TemplateOrSpecializationInfo =
llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *>;
+ bool isIncrementalErrorRecoveryMode() const {
+ return IncrementalErrorRecoveryMode;
+ }
+
private:
friend class ASTDeclReader;
friend class ASTReader;
friend class ASTWriter;
template <class> friend class serialization::AbstractTypeReader;
friend class CXXRecordDecl;
- friend class IncrementalParser;
+ // friend class IncrementalParser;
+ friend class ASTContextStateStash;
/// A mapping to contain the template or declaration that
/// a variable declaration describes or was instantiated from,
@@ -1359,6 +1375,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
TUDecl = NewTUDecl;
}
+ void setTranslationUnitDecl(TranslationUnitDecl *NewTUDecl) {
+ TUDecl = NewTUDecl;
+ }
+
ExternCContextDecl *getExternCContextDecl() const;
#define BuiltinTemplate(BTName) BuiltinTemplateDecl *get##BTName##Decl() const;
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index c64dc8c02d40e..d5b7f6240d134 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -3644,9 +3644,12 @@ class IndirectFieldDecl : public ValueDecl,
static bool classofKind(Kind K) { return K == IndirectField; }
};
+class ASTContextStateStash;
+
/// Represents a declaration of a type.
class TypeDecl : public NamedDecl {
friend class ASTContext;
+ friend class ASTContextStateStash;
friend class ASTReader;
/// This indicates the Type object that represents
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 8ea2533825dd9..39082a8973b2b 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -76,6 +76,8 @@ enum AvailabilityResult {
AR_Unavailable
};
+class ASTContextStateStash;
+
/// Decl - This represents one declaration (or definition), e.g. a variable,
/// typedef, function, struct, etc.
///
@@ -258,6 +260,7 @@ class alignas(8) Decl {
private:
friend class DeclContext;
+ friend class ASTContextStateStash;
struct MultipleDC {
DeclContext *SemanticDC;
@@ -1446,6 +1449,8 @@ enum class OMPDeclareReductionInitKind;
enum class ObjCImplementationControl;
enum class LinkageSpecLanguageIDs;
+class ASTContextStateStash;
+
/// DeclContext - This is used only as base class of specific decl types that
/// can act as declaration contexts. These decls are (only the top classes
/// that directly derive from DeclContext are mentioned, not their subclasses):
@@ -1464,6 +1469,7 @@ enum class LinkageSpecLanguageIDs;
/// BlockDecl
/// CapturedDecl
class DeclContext {
+ friend class ASTContextStateStash;
/// For makeDeclVisibleInContextImpl
friend class ASTDeclReader;
/// For checking the new bits in the Serialization part.
diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h
index 87820b14d6188..f753e1e8f9eb4 100644
--- a/clang/include/clang/AST/DeclContextInternals.h
+++ b/clang/include/clang/AST/DeclContextInternals.h
@@ -301,10 +301,13 @@ class StoredDeclsList {
}
};
+class ASTContextStateStash;
+
class StoredDeclsMap
: public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> {
friend class ASTContext; // walks the chain deleting these
friend class DeclContext;
+ friend class ASTContextStateStash;
llvm::PointerIntPair<StoredDeclsMap*, 1> Previous;
public:
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
new file mode 100644
index 0000000000000..6d5605a9624e6
--- /dev/null
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -0,0 +1,300 @@
+//===--- ErrorRecovery.h - Errory Recovery Impl --------------*- C++
+//-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
+#define LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
+
+#include "clang/Sema/Sema.h"
+
+namespace clang {
+class ASTContext;
+class Sema;
+
+struct SemaStashCheckPoint {
+ /// Sema::*
+ llvm::SlabCheckPoint SemaBumpSlabCP;
+ size_t CachedFunctionScopeSize = 0;
+ size_t FunctionScopesSize = 0;
+ size_t Ident_superSize = 0;
+ size_t PragmaClangBSSSectionSize = 0;
+ size_t PragmaClangDataSectionSize = 0;
+ size_t PragmaClangRodataSectionSize = 0;
+ size_t PragmaClangRelroSectionSize = 0;
+ size_t PragmaClangTextSectionSize = 0;
+ size_t VtorDispStackSize = 0;
+ size_t AlignPackStackSize = 0;
+ size_t AlignPackIncludeStackSize = 0;
+ size_t DataSegStackSize = 0;
+ size_t BSSSegStackSize = 0;
+ size_t ConstSegStackSize = 0;
+ size_t CodeSegStackSize = 0;
+ size_t StrictGuardStackCheckStackSize = 0;
+ size_t FpPragmaStackSize = 0;
+ size_t FunctionToSectionMapSize = 0;
+ size_t PragmaAttributeStackSize = 0;
+ size_t MSFunctionNoBuiltinsSize = 0;
+ size_t PendingExportedNamesSize = 0;
+ size_t TypoCorrectedFunctionDefinitionsSize = 0;
+ size_t FlagBitsCacheSize = 0;
+ size_t AssignEnumCacheSize = 0;
+ size_t WeakUndeclaredIdentifiersSize = 0;
+ size_t ExtnameUndeclaredIdentifiersSize = 0;
+ size_t UnusedLocalTypedefNameCandidatesSize = 0;
+ Sema::UnusedFileScopedDeclsType::iterator UnusedFileScopedDeclsSize;
+ Sema::TentativeDefinitionsType::iterator TentativeDefinitionsSize;
+ size_t ExternalDeclarationsSize = 0;
+ size_t ParsingInitForAutoVarsSize = 0;
+ size_t DeclsToCheckForDeferredDiagsSize = 0;
+ size_t ShadowingDeclsSize = 0;
+ size_t WeakTopLevelDeclSize = 0;
+ Sema::ExtVectorDeclsType::iterator ExtVectorDeclsSize;
+ size_t VTableUsesSize = 0;
+ size_t VTablesUsedSize = 0;
+ size_t DelayedDllExportClassesSize = 0;
+ size_t DelayedDllExportMemberFunctionsSize = 0;
+ size_t InventedParameterInfosSize = 0;
+ size_t FieldCollectorSize = 0;
+ size_t UnusedPrivateFieldsSize = 0;
+ size_t PureVirtualClassDiagSetSize = 0;
+ Sema::DelegatingCtorDeclsType::iterator DelegatingCtorDeclsSize;
+ size_t StdNamespaceSize = 0;
+ size_t UnparsedDefaultArgLocsSize = 0;
+ size_t UndefinedButUsedSize = 0;
+ size_t SpecialMembersBeingDeclaredSize = 0;
+ size_t DelayedOverridingExceptionSpecChecksSize = 0;
+ size_t DelayedEquivalentExceptionSpecChecksSize = 0;
+ size_t MaybeODRUseExprsSize = 0;
+ size_t RefsMinusAssignmentsSize = 0;
+ size_t ExprCleanupObjectsSize = 0;
+ size_t ExprEvalContextsSize = 0;
+ size_t FailedImmediateInvocationsSize = 0;
+ size_t ImplicitlyRetainedSelfLocsSize = 0;
+ size_t DeleteExprsSize = 0;
+ size_t CurrentParameterCopyTypesSize = 0;
+ size_t AggregateDeductionCandidatesSize = 0;
+ size_t TypoCorrectionFailuresSize = 0;
+ size_t SpecialMemberCacheSize = 0;
+ size_t ModuleScopesSize = 0;
+ size_t DeferredExportedNamespacesSize = 0;
+ size_t PendingInlineFuncDeclsSize = 0;
+ size_t CurrentSEHFinallySize = 0;
+ size_t CurrentDeferSize = 0;
+ size_t LateParsedTemplateMapSize = 0;
+ size_t SuppressedDiagnosticsSize = 0;
+ size_t CurrentInstantiationScopeSize = 0;
+ size_t UnparsedDefaultArgInstantiationsSize = 0;
+ size_t CodeSynthesisContextsSize = 0;
+ size_t InstantiatingSpecializationsSize = 0;
+ size_t InstantiatedNonDependentTypesSize = 0;
+ size_t CodeSynthesisContextLookupModulesSize = 0;
+ size_t LookupModulesCacheSize = 0;
+ size_t VisibleNamespaceCacheSize = 0;
+ size_t TemplateInstCallbacksSize = 0;
+ size_t PendingInstantiationsSize = 0;
+ size_t LateParsedInstantiationsSize = 0;
+ size_t SavedVTableUsesSize = 0;
+ size_t SavedPendingInstantiationsSize = 0;
+ size_t PendingLocalImplicitInstantiationsSize = 0;
+ size_t UnsubstitutedConstraintSatisfactionCacheSize = 0;
+ size_t SubsumptionCacheSize = 0;
+ size_t NormalizationCacheSize = 0;
+ size_t SatisfactionCacheSize = 0;
+ size_t SatisfactionStackSize = 0;
+// size_t NullabilityMapSize = 0;
+ size_t DeclsWithEffectsToVerifySize = 0;
+ // size_t AllEffectsToVerifySize = 0;
+};
+
+/// Stashes and restores persistent Sema state around an incremental parse.
+///
+/// Usage:
+/// SemaStashCheckPoint CP;
+/// SemaStateStash Stash(S);
+/// Stash.stash(CP);
+/// // ... parse ...
+/// if (failed)
+/// Stash.restore(CP, ASTSlabCP);
+class SemaStateStash {
+ Sema &S;
+
+ /// Holds full copies of PragmaStack, PragmaClangSection, FileNullabilityMap,
+ /// etc.
+ // struct PragmaSnapshot;
+ // std::unique_ptr<PragmaSnapshot> Pragmas;
+
+public:
+ explicit SemaStateStash(Sema &S) : S(S) {}
+ // ~SemaStateStash();
+
+ // SemaStateStash(const SemaStateStash &) = delete;
+ // SemaStateStash &operator=(const SemaStateStash &) = delete;
+
+ void stash(SemaStashCheckPoint &CP);
+ void restore(SemaStashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
+};
+
+struct StashCheckPoint {
+ // Types vector
+ size_t TypesSize = 0;
+
+ // FoldingSets — store count of nodes
+ size_t ExtQualNodesSize = 0;
+ size_t ComplexTypesSize = 0;
+ size_t PointerTypesSize = 0;
+ size_t AdjustedTypesSize = 0;
+ size_t BlockPointerTypesSize = 0;
+ size_t LValueReferenceTypesSize = 0;
+ size_t RValueReferenceTypesSize = 0;
+ size_t MemberPointerTypesSize = 0;
+
+ size_t ConstantArrayTypesSize = 0;
+ size_t IncompleteArrayTypesSize = 0;
+ size_t VariableArrayTypesSize = 0;
+
+ size_t DependentSizedArrayTypesSize = 0;
+ size_t DependentSizedExtVectorTypesSize = 0;
+ size_t DependentAddressSpaceTypesSize = 0;
+ size_t VectorTypesSize = 0;
+ size_t DependentVectorTypesSize = 0;
+ size_t MatrixTypesSize = 0;
+ size_t DependentSizedMatrixTypesSize = 0;
+ size_t FunctionNoProtoTypesSize = 0;
+ size_t FunctionProtoTypesSize = 0;
+ size_t DependentTypeOfExprTypesSize = 0;
+ size_t DependentDecltypeTypesSize = 0;
+
+ size_t DependentPackIndexingTypesSize = 0;
+
+ size_t TemplateTypeParmTypesSize = 0;
+ size_t ObjCTypeParamTypesSize = 0;
+ size_t SubstTemplateTypeParmTypesSize = 0;
+ size_t SubstTemplateTypeParmPackTypesSize = 0;
+ size_t SubstBuiltinTemplatePackTypesSize = 0;
+
+ size_t TemplateSpecializationTypesSize = 0;
+ size_t ParenTypesSize = 0;
+ size_t TagTypesSize = 0;
+ size_t UnresolvedUsingTypesSize = 0;
+ size_t UsingTypesSize = 0;
+ size_t TypedefTypesSize = 0;
+ size_t DependentNameTypesSize = 0;
+ size_t PackExpansionTypesSize = 0;
+ size_t ObjCObjectTypesSize = 0;
+ size_t ObjCObjectPointerTypesSize = 0;
+ size_t UnaryTransformTypesSize = 0;
+
+ size_t AutoTypesSize = 0;
+ size_t DeducedTemplateSpecializationTypesSize = 0;
+ size_t AtomicTypesSize = 0;
+ size_t AttributedTypesSize = 0;
+ size_t PipeTypesSize = 0;
+ size_t BitIntTypesSize = 0;
+ size_t DependentBitIntTypesSize = 0;
+ size_t BTFTagAttributedTypesSize = 0;
+ size_t HLSLAttributedResourceTypesSize = 0;
+ size_t HLSLInlineSpirvTypesSize = 0;
+
+ size_t CountAttributedTypesSize = 0;
+
+ size_t QualifiedTemplateNamesSize = 0;
+ size_t DependentTemplateNamesSize = 0;
+ size_t SubstTemplateTemplateParmsSize = 0;
+ size_t SubstTemplateTemplateParmPacksSize = 0;
+ size_t DeducedTemplatesSize = 0;
+
+ size_t ArrayParameterTypesSize = 0;
+
+ size_t PredefinedSugarTypesSize = 0;
+
+ size_t NamespaceAndPrefixStoragesSize = 0;
+
+ size_t ASTRecordLayoutsSize = 0;
+
+ size_t MemoizedTypeInfoSize = 0;
+
+ size_t MemoizedUnadjustedAlignSize = 0;
+
+ size_t KeyFunctionsSize = 0;
+
+ size_t BlockVarCopyInitsSize = 0;
+
+ size_t MSGuidDeclsSize = 0;
+
+ size_t UnnamedGlobalConstantDeclsSize = 0;
+
+ size_t TemplateParamObjectDeclsSize = 0;
+
+ size_t StringLiteralCacheSize = 0;
+
+ size_t DestroyingOperatorDeletesSize = 0;
+ size_t TypeAwareOperatorNewAndDeletesSize = 0;
+
+ size_t OperatorDeletesForVirtualDtorSize = 0;
+
+ size_t GlobalOperatorDeletesForVirtualDtorSize = 0;
+
+ size_t ArrayOperatorDeletesForVirtualDtorSize = 0;
+ size_t GlobalArrayOperatorDeletesForVirtualDtorSize = 0;
+
+ size_t RequireVectorDeletingDtorSize = 0;
+
+ size_t MergedDeclsSize = 0;
+ size_t MergedDefModulesSize = 0;
+
+ size_t ModuleInitializersSize = 0;
+ size_t PrimaryModuleNameMapSize = 0;
+ size_t SameModuleLookupSetSize = 0;
+
+ size_t ScalableVecTyMapSize = 0;
+ size_t LambdaCastPathsSize = 0;
+ size_t DeclRawCommentsSize = 0;
+ size_t RedeclChainCommentsSize = 0;
+ size_t CommentlessRedeclChainsSize = 0;
+ size_t ParsedCommentsSize = 0;
+ size_t RelocatableClassesSize = 0;
+ size_t ParamIndicesSize = 0;
+ size_t MangleNumbersSize = 0;
+ size_t StaticLocalNumbersSize = 0;
+ size_t TemplateOrInstantiationSize = 0;
+
+ size_t InstantiatedFromUsingDeclSize = 0;
+ size_t InstantiatedFromUsingEnumDeclSize = 0;
+
+ size_t InstantiatedFromUsingShadowDeclSize = 0;
+
+ size_t InstantiatedFromUnnamedFieldDeclSize = 0;
+ size_t OverriddenMethodsSize = 0;
+ size_t MangleNumberingContextsSize = 0;
+ size_t ExtraMangleNumberingContextsSize = 0;
+
+ size_t TraversalScopeSize = 0;
+ llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
+// = llvm::PointerIntPair<StoredDeclsMap *, 1>(nullptr, 0);
+};
+
+class ASTContextStateStash {
+private:
+ ASTContext &Ctx;
+
+public:
+ explicit ASTContextStateStash(ASTContext &Ctx) : Ctx(Ctx) {}
+
+ ASTContextStateStash(const ASTContextStateStash &) = delete;
+ ASTContextStateStash &operator=(const ASTContextStateStash &) = delete;
+
+ void stash(StashCheckPoint &CP);
+ void restore(StashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
+ void commit();
+};
+
+} // end namespace clang
+#endif // LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
diff --git a/clang/include/clang/Interpreter/PartialTranslationUnit.h b/clang/include/clang/Interpreter/PartialTranslationUnit.h
index c878e139fe70d..843f20f82e3bc 100644
--- a/clang/include/clang/Interpreter/PartialTranslationUnit.h
+++ b/clang/include/clang/Interpreter/PartialTranslationUnit.h
@@ -14,6 +14,8 @@
#ifndef LLVM_CLANG_INTERPRETER_PARTIALTRANSLATIONUNIT_H
#define LLVM_CLANG_INTERPRETER_PARTIALTRANSLATIONUNIT_H
+#include "llvm/Support/Allocator.h"
+
#include <memory>
namespace llvm {
@@ -28,6 +30,7 @@ class TranslationUnitDecl;
/// incremental inputs.
struct PartialTranslationUnit {
TranslationUnitDecl *TUPart = nullptr;
+ llvm::SlabCheckPoint SlabCheckPoint;
/// The llvm IR produced for the input.
std::unique_ptr<llvm::Module> TheModule;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e33073c652dba..feb1b9eea5c7b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -186,6 +186,7 @@ class SemaSystemZ;
class SemaWasm;
class SemaX86;
class StandardConversionSequence;
+class SemaStateStash;
class TemplateArgument;
class TemplateArgumentLoc;
class TemplateInstantiationCallback;
@@ -1587,6 +1588,7 @@ class Sema final : public SemaBase {
friend class ASTReader;
friend class ASTDeclReader;
friend class ASTWriter;
+ friend class SemaStateStash;
private:
std::optional<std::unique_ptr<DarwinSDKInfo>> CachedDarwinSDKInfo;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a361b07e201b3..e850e9a0b61d1 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5621,6 +5621,10 @@ QualType ASTContext::getTagType(ElaboratedTypeKeyword Keyword,
/*Qualifier=*/std::nullopt, NonInjectedTD,
/*OwnsTag=*/false, IsInjected, CanonicalType,
/*WithFoldingSetNode=*/false);
+
+ if (IncrementalErrorRecoveryMode)
+ PendingTypeForDeclMutations.push_back({const_cast<TagDecl *>(TD), TD->TypeForDecl});
+
TD->TypeForDecl = T;
return QualType(T, 0);
}
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 548643cea2cce..4bc4b8ca2acdc 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -2211,6 +2211,9 @@ void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D, bool Internal) {
}
DeclNameEntries.addOrReplaceDecl(D);
+
+ if (getParentASTContext().isIncrementalErrorRecoveryMode())
+ getParentASTContext().PendingDCMutations.insert(this);
}
UsingDirectiveDecl *DeclContext::udir_iterator::operator*() const {
diff --git a/clang/lib/Interpreter/ASTContextStateStash.cpp b/clang/lib/Interpreter/ASTContextStateStash.cpp
new file mode 100644
index 0000000000000..18ad727ddc5ef
--- /dev/null
+++ b/clang/lib/Interpreter/ASTContextStateStash.cpp
@@ -0,0 +1,1362 @@
+//===--- ASTContextStateStash.cpp - ASTContext persistent state stash/restore
+//----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Interpreter/ErrorRecovery.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclContextInternals.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Type.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang {
+
+/// Erases nodes from a FoldingSet based on a predicate.
+template <typename EntryType, typename PredT>
+static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
+ SmallVector<EntryType *, 16> ToRemove;
+
+ for (auto &N : FS)
+ if (Pred(N))
+ ToRemove.push_back(&N);
+
+ for (auto *N : ToRemove)
+ FS.RemoveNode(N);
+}
+
+template <typename EntryType, typename PredT>
+static void
+eraseFoldingSetIf(llvm::ContextualFoldingSet<EntryType, ASTContext &> &FS,
+ PredT &&Pred) {
+ SmallVector<EntryType *, 16> ToRemove;
+
+ for (auto &N : FS)
+ if (Pred(N))
+ ToRemove.push_back(&N);
+
+ for (auto *N : ToRemove)
+ FS.RemoveNode(N);
+}
+
+/// Erase from DenseMap based on predicate
+template <typename KeyT, typename ValueT, typename PredT>
+static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
+ SmallVector<KeyT, 16> ToRemove;
+
+ for (auto &KV : Map)
+ if (Pred(KV))
+ ToRemove.push_back(KV.getFirst());
+
+ for (auto &Key : ToRemove)
+ Map.erase(Key);
+}
+
+/// Erase from DenseMap based on predicate
+template <typename ValueT, typename PredT>
+static void eraseDenseSetIf(llvm::DenseSet<ValueT> &Set, PredT &&Pred) {
+ SmallVector<ValueT, 16> ToRemove;
+
+ for (auto &V : Set)
+ if (Pred(V))
+ ToRemove.push_back(V);
+
+ for (auto &Key : ToRemove)
+ Set.erase(Key);
+}
+
+void ASTContextStateStash::stash(StashCheckPoint &CP) {
+ CP.TypesSize = Ctx.Types.size();
+ CP.ExtQualNodesSize = Ctx.ExtQualNodes.size();
+ CP.ComplexTypesSize = Ctx.ComplexTypes.size();
+ CP.PointerTypesSize = Ctx.PointerTypes.size();
+ CP.AdjustedTypesSize = Ctx.AdjustedTypes.size();
+ CP.BlockPointerTypesSize = Ctx.BlockPointerTypes.size();
+ CP.LValueReferenceTypesSize = Ctx.LValueReferenceTypes.size();
+ CP.RValueReferenceTypesSize = Ctx.RValueReferenceTypes.size();
+ CP.MemberPointerTypesSize = Ctx.MemberPointerTypes.size();
+
+ CP.ConstantArrayTypesSize = Ctx.ConstantArrayTypes.size();
+ CP.IncompleteArrayTypesSize = Ctx.IncompleteArrayTypes.size();
+ CP.VariableArrayTypesSize = Ctx.VariableArrayTypes.size();
+
+ CP.DependentSizedArrayTypesSize = Ctx.DependentSizedArrayTypes.size();
+ CP.DependentSizedExtVectorTypesSize = Ctx.DependentSizedExtVectorTypes.size();
+ CP.DependentAddressSpaceTypesSize = Ctx.DependentAddressSpaceTypes.size();
+ CP.VectorTypesSize = Ctx.VectorTypes.size();
+ CP.DependentVectorTypesSize = Ctx.DependentVectorTypes.size();
+ CP.MatrixTypesSize = Ctx.MatrixTypes.size();
+ CP.DependentSizedMatrixTypesSize = Ctx.DependentSizedMatrixTypes.size();
+ CP.FunctionNoProtoTypesSize = Ctx.FunctionNoProtoTypes.size();
+ CP.FunctionProtoTypesSize = Ctx.FunctionProtoTypes.size();
+ CP.DependentTypeOfExprTypesSize = Ctx.DependentTypeOfExprTypes.size();
+ CP.DependentDecltypeTypesSize = Ctx.DependentDecltypeTypes.size();
+
+ CP.DependentPackIndexingTypesSize = Ctx.DependentPackIndexingTypes.size();
+
+ CP.TemplateTypeParmTypesSize = Ctx.TemplateTypeParmTypes.size();
+ CP.ObjCTypeParamTypesSize = Ctx.ObjCTypeParamTypes.size();
+ CP.SubstTemplateTypeParmTypesSize = Ctx.SubstTemplateTypeParmTypes.size();
+ CP.SubstTemplateTypeParmPackTypesSize =
+ Ctx.SubstTemplateTypeParmPackTypes.size();
+ CP.SubstBuiltinTemplatePackTypesSize =
+ Ctx.SubstBuiltinTemplatePackTypes.size();
+
+ CP.TemplateSpecializationTypesSize = Ctx.TemplateSpecializationTypes.size();
+ CP.ParenTypesSize = Ctx.ParenTypes.size();
+ CP.TagTypesSize = Ctx.TagTypes.size();
+ CP.UnresolvedUsingTypesSize = Ctx.UnresolvedUsingTypes.size();
+ CP.UsingTypesSize = Ctx.UsingTypes.size();
+ CP.TypedefTypesSize = Ctx.TypedefTypes.size();
+ CP.DependentNameTypesSize = Ctx.DependentNameTypes.size();
+ CP.PackExpansionTypesSize = Ctx.PackExpansionTypes.size();
+ CP.ObjCObjectTypesSize = Ctx.ObjCObjectTypes.size();
+ CP.ObjCObjectPointerTypesSize = Ctx.ObjCObjectPointerTypes.size();
+ CP.UnaryTransformTypesSize = Ctx.UnaryTransformTypes.size();
+
+ CP.AutoTypesSize = Ctx.AutoTypes.size();
+ CP.DeducedTemplateSpecializationTypesSize =
+ Ctx.DeducedTemplateSpecializationTypes.size();
+ CP.AtomicTypesSize = Ctx.AtomicTypes.size();
+ CP.AttributedTypesSize = Ctx.AttributedTypes.size();
+ CP.PipeTypesSize = Ctx.PipeTypes.size();
+ CP.BitIntTypesSize = Ctx.BitIntTypes.size();
+ CP.DependentBitIntTypesSize = Ctx.DependentBitIntTypes.size();
+ CP.BTFTagAttributedTypesSize = Ctx.BTFTagAttributedTypes.size();
+ CP.HLSLAttributedResourceTypesSize = Ctx.HLSLAttributedResourceTypes.size();
+ CP.HLSLInlineSpirvTypesSize = Ctx.HLSLInlineSpirvTypes.size();
+
+ CP.CountAttributedTypesSize = Ctx.CountAttributedTypes.size();
+
+ CP.QualifiedTemplateNamesSize = Ctx.QualifiedTemplateNames.size();
+ CP.DependentTemplateNamesSize = Ctx.DependentTemplateNames.size();
+ CP.SubstTemplateTemplateParmsSize = Ctx.SubstTemplateTemplateParms.size();
+ CP.SubstTemplateTemplateParmPacksSize =
+ Ctx.SubstTemplateTemplateParmPacks.size();
+ CP.DeducedTemplatesSize = Ctx.DeducedTemplates.size();
+
+ CP.ArrayParameterTypesSize = Ctx.ArrayParameterTypes.size();
+
+ CP.PredefinedSugarTypesSize = Ctx.PredefinedSugarTypes.size();
+
+ CP.NamespaceAndPrefixStoragesSize = Ctx.NamespaceAndPrefixStorages.size();
+
+ CP.ASTRecordLayoutsSize = Ctx.ASTRecordLayouts.size();
+
+ CP.MemoizedTypeInfoSize = Ctx.MemoizedTypeInfo.size();
+
+ CP.MemoizedUnadjustedAlignSize = Ctx.MemoizedUnadjustedAlign.size();
+
+ CP.KeyFunctionsSize = Ctx.KeyFunctions.size();
+
+ CP.BlockVarCopyInitsSize = Ctx.BlockVarCopyInits.size();
+
+ CP.MSGuidDeclsSize = Ctx.MSGuidDecls.size();
+
+ CP.UnnamedGlobalConstantDeclsSize = Ctx.UnnamedGlobalConstantDecls.size();
+
+ CP.TemplateParamObjectDeclsSize = Ctx.TemplateParamObjectDecls.size();
+
+ CP.StringLiteralCacheSize = Ctx.StringLiteralCache.size();
+
+ CP.DestroyingOperatorDeletesSize = Ctx.DestroyingOperatorDeletes.size();
+ CP.TypeAwareOperatorNewAndDeletesSize =
+ Ctx.TypeAwareOperatorNewAndDeletes.size();
+
+ CP.OperatorDeletesForVirtualDtorSize =
+ Ctx.OperatorDeletesForVirtualDtor.size();
+
+ CP.GlobalOperatorDeletesForVirtualDtorSize =
+ Ctx.GlobalOperatorDeletesForVirtualDtor.size();
+
+ CP.ArrayOperatorDeletesForVirtualDtorSize =
+ Ctx.ArrayOperatorDeletesForVirtualDtor.size();
+ CP.GlobalArrayOperatorDeletesForVirtualDtorSize =
+ Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size();
+
+ CP.RequireVectorDeletingDtorSize = Ctx.RequireVectorDeletingDtor.size();
+
+ CP.MergedDeclsSize = Ctx.MergedDecls.size();
+ CP.MergedDefModulesSize = Ctx.MergedDefModules.size();
+
+ CP.ModuleInitializersSize = Ctx.ModuleInitializers.size();
+ CP.PrimaryModuleNameMapSize = Ctx.PrimaryModuleNameMap.size();
+ CP.SameModuleLookupSetSize = Ctx.SameModuleLookupSet.size();
+
+ CP.ScalableVecTyMapSize = Ctx.ScalableVecTyMap.size();
+ CP.LambdaCastPathsSize = Ctx.LambdaCastPaths.size();
+ CP.DeclRawCommentsSize = Ctx.DeclRawComments.size();
+ CP.RedeclChainCommentsSize = Ctx.RedeclChainComments.size();
+ CP.CommentlessRedeclChainsSize = Ctx.CommentlessRedeclChains.size();
+ CP.ParsedCommentsSize = Ctx.ParsedComments.size();
+ CP.RelocatableClassesSize = Ctx.RelocatableClasses.size();
+ CP.ParamIndicesSize = Ctx.ParamIndices.size();
+ CP.MangleNumbersSize = Ctx.MangleNumbers.size();
+ CP.StaticLocalNumbersSize = Ctx.StaticLocalNumbers.size();
+ CP.TemplateOrInstantiationSize = Ctx.TemplateOrInstantiation.size();
+
+ CP.InstantiatedFromUsingDeclSize = Ctx.InstantiatedFromUsingDecl.size();
+ CP.InstantiatedFromUsingEnumDeclSize =
+ Ctx.InstantiatedFromUsingEnumDecl.size();
+
+ CP.InstantiatedFromUsingShadowDeclSize =
+ Ctx.InstantiatedFromUsingShadowDecl.size();
+
+ CP.InstantiatedFromUnnamedFieldDeclSize =
+ Ctx.InstantiatedFromUnnamedFieldDecl.size();
+ CP.OverriddenMethodsSize = Ctx.OverriddenMethods.size();
+ CP.MangleNumberingContextsSize = Ctx.MangleNumberingContexts.size();
+ CP.ExtraMangleNumberingContextsSize = Ctx.ExtraMangleNumberingContexts.size();
+ CP.TraversalScopeSize = Ctx.TraversalScope.size();
+ CP.LastSDM = Ctx.LastSDM;
+}
+
+void ASTContextStateStash::restore(StashCheckPoint &CP,
+ llvm::SlabCheckPoint SlabCP) {
+
+ if (Ctx.TemplateTypeParmTypes.size() != CP.TemplateTypeParmTypesSize) {
+ llvm::dbgs() << "Ctx.TemplateTypeParmTypes.size() != "
+ "CP.TemplateTypeParmTypesSize\n";
+ eraseFoldingSetIf(Ctx.TemplateTypeParmTypes,
+ [&](TemplateTypeParmType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.TemplateTypeParmTypes.size() == CP.TemplateTypeParmTypesSize);
+ }
+
+ if (Ctx.TagTypes.size() != CP.TagTypesSize) {
+ llvm::dbgs() << "Ctx.TagTypes.size() != CP.TagTypesSize\n";
+ eraseFoldingSetIf(Ctx.TagTypes,
+ [&](TagTypeFoldingSetPlaceholder &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+
+ assert(Ctx.TagTypes.size() == CP.TagTypesSize);
+ }
+
+ if (Ctx.ExtQualNodes.size() != CP.ExtQualNodesSize) {
+ llvm::dbgs() << "Ctx.ExtQualNodes.size() != CP.ExtQualNodesSize\n";
+ eraseFoldingSetIf(Ctx.ExtQualNodes, [&](ExtQuals &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.ExtQualNodes.size() == CP.ExtQualNodesSize);
+ }
+
+ if (Ctx.ComplexTypes.size() != CP.ComplexTypesSize) {
+ llvm::dbgs() << "Ctx.ComplexTypes.size() != CP.ComplexTypesSize\n";
+ eraseFoldingSetIf(Ctx.ComplexTypes, [&](ComplexType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.ComplexTypes.size() == CP.ComplexTypesSize);
+ }
+
+ if (Ctx.PointerTypes.size() != CP.PointerTypesSize) {
+ llvm::dbgs() << "Ctx.PointerTypes.size() != CP.PointerTypesSize\n";
+ eraseFoldingSetIf(Ctx.PointerTypes, [&](PointerType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.PointerTypes.size() == CP.PointerTypesSize);
+ }
+
+ if (Ctx.AdjustedTypes.size() != CP.AdjustedTypesSize) {
+ llvm::dbgs() << "Ctx.AdjustedTypes.size() != CP.AdjustedTypesSize\n";
+ eraseFoldingSetIf(Ctx.AdjustedTypes, [&](AdjustedType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.AdjustedTypes.size() == CP.AdjustedTypesSize);
+ }
+
+ if (Ctx.BlockPointerTypes.size() != CP.BlockPointerTypesSize) {
+ llvm::dbgs()
+ << "Ctx.BlockPointerTypes.size() != CP.BlockPointerTypesSize\n";
+ eraseFoldingSetIf(Ctx.BlockPointerTypes,
+ [&](BlockPointerType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.BlockPointerTypes.size() == CP.BlockPointerTypesSize);
+ }
+
+ if (Ctx.LValueReferenceTypes.size() != CP.LValueReferenceTypesSize) {
+ llvm::dbgs()
+ << "Ctx.LValueReferenceTypes.size() != CP.LValueReferenceTypesSize\n";
+ eraseFoldingSetIf(Ctx.LValueReferenceTypes,
+ [&](LValueReferenceType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.LValueReferenceTypes.size() == CP.LValueReferenceTypesSize);
+ }
+
+ if (Ctx.RValueReferenceTypes.size() != CP.RValueReferenceTypesSize) {
+ llvm::dbgs()
+ << "Ctx.RValueReferenceTypes.size() != CP.RValueReferenceTypesSize\n";
+ eraseFoldingSetIf(Ctx.RValueReferenceTypes,
+ [&](RValueReferenceType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.RValueReferenceTypes.size() == CP.RValueReferenceTypesSize);
+ }
+
+ if (Ctx.MemberPointerTypes.size() != CP.MemberPointerTypesSize) {
+ llvm::dbgs()
+ << "Ctx.MemberPointerTypes.size() != CP.MemberPointerTypesSize\n";
+ eraseFoldingSetIf(Ctx.MemberPointerTypes,
+ [&](MemberPointerType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.MemberPointerTypes.size() == CP.MemberPointerTypesSize);
+ }
+
+ if (Ctx.ConstantArrayTypes.size() != CP.ConstantArrayTypesSize) {
+ llvm::dbgs()
+ << "Ctx.ConstantArrayTypes.size() != CP.ConstantArrayTypesSize\n";
+ eraseFoldingSetIf(Ctx.ConstantArrayTypes,
+ [&](ConstantArrayType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.ConstantArrayTypes.size() == CP.ConstantArrayTypesSize);
+ }
+
+ if (Ctx.IncompleteArrayTypes.size() != CP.IncompleteArrayTypesSize) {
+ llvm::dbgs()
+ << "Ctx.IncompleteArrayTypes.size() != CP.IncompleteArrayTypesSize\n";
+ eraseFoldingSetIf(Ctx.IncompleteArrayTypes,
+ [&](IncompleteArrayType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.IncompleteArrayTypes.size() == CP.IncompleteArrayTypesSize);
+ }
+
+ // mutable std::vector<VariableArrayType*> VariableArrayTypes;
+ if (Ctx.VariableArrayTypes.size() != CP.VariableArrayTypesSize) {
+ llvm::dbgs()
+ << "Ctx.VariableArrayTypes.size() != CP.VariableArrayTypesSize\n";
+ Ctx.VariableArrayTypes.resize(CP.VariableArrayTypesSize);
+ assert(Ctx.VariableArrayTypes.size() == CP.VariableArrayTypesSize);
+ }
+
+ if (Ctx.DependentSizedArrayTypes.size() != CP.DependentSizedArrayTypesSize) {
+ llvm::dbgs() << "Ctx.DependentSizedArrayTypes.size() != "
+ "CP.DependentSizedArrayTypesSize\n";
+ eraseFoldingSetIf(Ctx.DependentSizedArrayTypes,
+ [&](DependentSizedArrayType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentSizedArrayTypes.size() ==
+ CP.DependentSizedArrayTypesSize);
+ }
+
+ if (Ctx.DependentSizedExtVectorTypes.size() !=
+ CP.DependentSizedExtVectorTypesSize) {
+ llvm::dbgs() << "Ctx.DependentSizedExtVectorTypes.size() != "
+ "CP.DependentSizedExtVectorTypesSize\n";
+ eraseFoldingSetIf(Ctx.DependentSizedExtVectorTypes,
+ [&](DependentSizedExtVectorType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentSizedExtVectorTypes.size() ==
+ CP.DependentSizedExtVectorTypesSize);
+ }
+
+ if (Ctx.DependentAddressSpaceTypes.size() !=
+ CP.DependentAddressSpaceTypesSize) {
+ llvm::dbgs() << "Ctx.DependentAddressSpaceTypes.size() != "
+ "CP.DependentAddressSpaceTypesSize\n";
+
+ eraseFoldingSetIf(Ctx.DependentAddressSpaceTypes,
+ [&](DependentAddressSpaceType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentAddressSpaceTypes.size() ==
+ CP.DependentAddressSpaceTypesSize);
+ }
+
+ if (Ctx.VectorTypes.size() != CP.VectorTypesSize) {
+ llvm::dbgs() << "Ctx.VectorTypes.size() != CP.VectorTypesSize\n";
+ eraseFoldingSetIf(Ctx.VectorTypes, [&](VectorType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.VectorTypes.size() == CP.VectorTypesSize);
+ }
+
+ if (Ctx.DependentVectorTypes.size() != CP.DependentVectorTypesSize) {
+ llvm::dbgs()
+ << "Ctx.DependentVectorTypes.size() != CP.DependentVectorTypesSize\n";
+ eraseFoldingSetIf(Ctx.DependentVectorTypes,
+ [&](DependentVectorType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentVectorTypes.size() == CP.DependentVectorTypesSize);
+ }
+
+ if (Ctx.MatrixTypes.size() != CP.MatrixTypesSize) {
+ llvm::dbgs() << "Ctx.MatrixTypes.size() != CP.MatrixTypesSize\n";
+ eraseFoldingSetIf(Ctx.MatrixTypes, [&](ConstantMatrixType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.MatrixTypes.size() == CP.MatrixTypesSize);
+ }
+
+ if (Ctx.DependentSizedMatrixTypes.size() !=
+ CP.DependentSizedMatrixTypesSize) {
+ llvm::dbgs() << "Ctx.DependentSizedMatrixTypes.size() != "
+ "CP.DependentSizedMatrixTypesSize\n";
+ eraseFoldingSetIf(Ctx.DependentSizedMatrixTypes,
+ [&](DependentSizedMatrixType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentSizedMatrixTypes.size() ==
+ CP.DependentSizedMatrixTypesSize);
+ }
+
+ if (Ctx.FunctionNoProtoTypes.size() != CP.FunctionNoProtoTypesSize) {
+ llvm::dbgs()
+ << "Ctx.FunctionNoProtoTypes.size() != CP.FunctionNoProtoTypesSize\n";
+ eraseFoldingSetIf(Ctx.FunctionNoProtoTypes,
+ [&](FunctionNoProtoType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.FunctionNoProtoTypes.size() == CP.FunctionNoProtoTypesSize);
+ }
+
+ if (Ctx.FunctionProtoTypes.size() != CP.FunctionProtoTypesSize) {
+ llvm::dbgs()
+ << "Ctx.FunctionProtoTypes.size() != CP.FunctionProtoTypesSize\n";
+ eraseFoldingSetIf(Ctx.FunctionProtoTypes,
+ [&](FunctionProtoType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.FunctionProtoTypes.size() == CP.FunctionProtoTypesSize);
+ }
+
+ if (Ctx.DependentTypeOfExprTypes.size() != CP.DependentTypeOfExprTypesSize) {
+ llvm::dbgs() << "Ctx.DependentTypeOfExprTypes.size() != "
+ "CP.DependentTypeOfExprTypesSize\n";
+ eraseFoldingSetIf(Ctx.DependentTypeOfExprTypes,
+ [&](DependentTypeOfExprType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentTypeOfExprTypes.size() ==
+ CP.DependentTypeOfExprTypesSize);
+ }
+
+ if (Ctx.DependentDecltypeTypes.size() != CP.DependentDecltypeTypesSize) {
+ llvm::dbgs() << "Ctx.DependentDecltypeTypes.size() != "
+ "CP.DependentDecltypeTypesSize\n";
+ eraseFoldingSetIf(Ctx.DependentDecltypeTypes,
+ [&](DependentDecltypeType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentDecltypeTypes.size() == CP.DependentDecltypeTypesSize);
+ }
+
+ if (Ctx.DependentPackIndexingTypes.size() !=
+ CP.DependentPackIndexingTypesSize) {
+ llvm::dbgs() << "Ctx.DependentPackIndexingTypes.size() != "
+ "CP.DependentPackIndexingTypesSize\n";
+ eraseFoldingSetIf(Ctx.DependentPackIndexingTypes,
+ [&](PackIndexingType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentPackIndexingTypes.size() ==
+ CP.DependentPackIndexingTypesSize);
+ }
+
+ if (Ctx.TemplateTypeParmTypes.size() != CP.TemplateTypeParmTypesSize) {
+ llvm::dbgs() << "Ctx.TemplateTypeParmTypes.size() != "
+ "CP.TemplateTypeParmTypesSize\n";
+ eraseFoldingSetIf(Ctx.TemplateTypeParmTypes,
+ [&](TemplateTypeParmType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.TemplateTypeParmTypes.size() == CP.TemplateTypeParmTypesSize);
+ }
+
+ // mutable llvm::FoldingSet<ObjCTypeParamType> ObjCTypeParamTypes;
+
+ if (Ctx.SubstTemplateTypeParmTypes.size() !=
+ CP.SubstTemplateTypeParmTypesSize) {
+ llvm::dbgs() << "Ctx.SubstTemplateTypeParmTypes.size() != "
+ "CP.SubstTemplateTypeParmTypesSize\n";
+ eraseFoldingSetIf(Ctx.SubstTemplateTypeParmTypes,
+ [&](SubstTemplateTypeParmType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.SubstTemplateTypeParmTypes.size() ==
+ CP.SubstTemplateTypeParmTypesSize);
+ }
+
+ if (Ctx.SubstTemplateTypeParmPackTypes.size() !=
+ CP.SubstTemplateTypeParmPackTypesSize) {
+ llvm::dbgs() << "Ctx.SubstTemplateTypeParmPackTypes.size() != "
+ "CP.SubstTemplateTypeParmPackTypesSize\n";
+ eraseFoldingSetIf(Ctx.SubstTemplateTypeParmPackTypes,
+ [&](SubstTemplateTypeParmPackType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.SubstTemplateTypeParmPackTypes.size() ==
+ CP.SubstTemplateTypeParmPackTypesSize);
+ }
+
+ if (Ctx.SubstBuiltinTemplatePackTypes.size() !=
+ CP.SubstBuiltinTemplatePackTypesSize) {
+ llvm::dbgs() << "Ctx.SubstBuiltinTemplatePackTypes.size() != "
+ "CP.SubstBuiltinTemplatePackTypesSize\n";
+ eraseFoldingSetIf(Ctx.SubstBuiltinTemplatePackTypes,
+ [&](SubstBuiltinTemplatePackType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.SubstBuiltinTemplatePackTypes.size() ==
+ CP.SubstBuiltinTemplatePackTypesSize);
+ }
+
+ if (Ctx.TemplateSpecializationTypes.size() !=
+ CP.TemplateSpecializationTypesSize) {
+ llvm::dbgs() << "Ctx.TemplateSpecializationTypes.size() != "
+ "CP.TemplateSpecializationTypesSize\n";
+ eraseFoldingSetIf(Ctx.TemplateSpecializationTypes,
+ [&](TemplateSpecializationType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.TemplateSpecializationTypes.size() ==
+ CP.TemplateSpecializationTypesSize);
+ }
+
+ if (Ctx.ParenTypes.size() != CP.ParenTypesSize) {
+ llvm::dbgs() << "Ctx.ParenTypes.size() != CP.ParenTypesSize\n";
+ eraseFoldingSetIf(Ctx.ParenTypes, [&](ParenType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.ParenTypes.size() == CP.ParenTypesSize);
+ }
+
+ if (Ctx.TagTypes.size() != CP.TagTypesSize) {
+ llvm::dbgs() << "Ctx.TagTypes.size() != CP.TagTypesSize\n";
+ eraseFoldingSetIf(Ctx.TagTypes,
+ [&](TagTypeFoldingSetPlaceholder &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.TagTypes.size() == CP.TagTypesSize);
+ }
+
+ if (Ctx.UnresolvedUsingTypes.size() != CP.UnresolvedUsingTypesSize) {
+ llvm::dbgs()
+ << "Ctx.UnresolvedUsingTypes.size() != CP.UnresolvedUsingTypesSize\n";
+ eraseFoldingSetIf(
+ Ctx.UnresolvedUsingTypes,
+ [&](FoldingSetPlaceholder<UnresolvedUsingType> &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.UnresolvedUsingTypes.size() == CP.UnresolvedUsingTypesSize);
+ }
+
+ if (Ctx.UsingTypes.size() != CP.UsingTypesSize) {
+ llvm::dbgs() << "Ctx.UsingTypes.size() != CP.UsingTypesSize\n";
+ eraseFoldingSetIf(Ctx.UsingTypes, [&](UsingType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.UsingTypes.size() == CP.UsingTypesSize);
+ }
+
+ if (Ctx.TypedefTypes.size() != CP.TypedefTypesSize) {
+ llvm::dbgs() << "Ctx.TypedefTypes.size() != CP.TypedefTypesSize\n";
+ eraseFoldingSetIf(Ctx.TypedefTypes,
+ [&](FoldingSetPlaceholder<TypedefType> &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.TypedefTypes.size() == CP.TypedefTypesSize);
+ }
+
+ if (Ctx.DependentNameTypes.size() != CP.DependentNameTypesSize) {
+ llvm::dbgs()
+ << "Ctx.DependentNameTypes.size() != CP.DependentNameTypesSize\n";
+ eraseFoldingSetIf(Ctx.DependentNameTypes,
+ [&](DependentNameType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentNameTypes.size() == CP.DependentNameTypesSize);
+ }
+
+ if (Ctx.PackExpansionTypes.size() != CP.PackExpansionTypesSize) {
+ llvm::dbgs()
+ << "Ctx.PackExpansionTypes.size() != CP.PackExpansionTypesSize\n";
+ eraseFoldingSetIf(Ctx.PackExpansionTypes,
+ [&](PackExpansionType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.PackExpansionTypes.size() == CP.PackExpansionTypesSize);
+ }
+
+ // mutable llvm::FoldingSet<ObjCObjectTypeImpl> ObjCObjectTypes;
+ // mutable llvm::FoldingSet<ObjCObjectPointerType> ObjCObjectPointerTypes;
+
+ if (Ctx.UnaryTransformTypes.size() != CP.UnaryTransformTypesSize) {
+ llvm::dbgs()
+ << "Ctx.UnaryTransformTypes.size() != CP.UnaryTransformTypesSize\n";
+ eraseFoldingSetIf(Ctx.UnaryTransformTypes,
+ [&](UnaryTransformType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.UnaryTransformTypes.size() == CP.UnaryTransformTypesSize);
+ }
+
+ if (Ctx.AutoTypes.size() != CP.AutoTypesSize) {
+ llvm::dbgs() << "Ctx.AutoTypes.size() != CP.AutoTypesSize\n";
+ // mutable llvm::DenseMap<llvm::FoldingSetNodeID, AutoType *> AutoTypes;
+ eraseDenseMapIf(
+ Ctx.AutoTypes,
+ [&](llvm::detail::DenseMapPair<llvm::FoldingSetNodeID, AutoType *> &KV)
+ -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(KV.getSecond()), SlabCP);
+ });
+ assert(Ctx.AutoTypes.size() == CP.AutoTypesSize);
+ }
+
+ if (Ctx.DeducedTemplateSpecializationTypes.size() !=
+ CP.DeducedTemplateSpecializationTypesSize) {
+ llvm::dbgs() << "Ctx.DeducedTemplateSpecializationTypes.size() != "
+ "CP.DeducedTemplateSpecializationTypesSize\n";
+ eraseFoldingSetIf(Ctx.DeducedTemplateSpecializationTypes,
+ [&](DeducedTemplateSpecializationType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DeducedTemplateSpecializationTypes.size() ==
+ CP.DeducedTemplateSpecializationTypesSize);
+ }
+
+ if (Ctx.AtomicTypes.size() != CP.AtomicTypesSize) {
+ llvm::dbgs() << "Ctx.AtomicTypes.size() != CP.AtomicTypesSize\n";
+ eraseFoldingSetIf(Ctx.AtomicTypes, [&](AtomicType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.AtomicTypes.size() == CP.AtomicTypesSize);
+ }
+
+ if (Ctx.AttributedTypes.size() != CP.AttributedTypesSize) {
+ llvm::dbgs() << "Ctx.AttributedTypes.size() != CP.AttributedTypesSize\n";
+ eraseFoldingSetIf(Ctx.AttributedTypes, [&](AttributedType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.AttributedTypes.size() == CP.AttributedTypesSize);
+ }
+
+ if (Ctx.PipeTypes.size() != CP.PipeTypesSize) {
+ llvm::dbgs() << "Ctx.PipeTypes.size() != CP.PipeTypesSize\n";
+ eraseFoldingSetIf(Ctx.PipeTypes, [&](PipeType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.PipeTypes.size() == CP.PipeTypesSize);
+ }
+
+ if (Ctx.BitIntTypes.size() != CP.BitIntTypesSize) {
+ llvm::dbgs() << "Ctx.BitIntTypes.size() != CP.BitIntTypesSize\n";
+ eraseFoldingSetIf(Ctx.BitIntTypes, [&](BitIntType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(Ctx.BitIntTypes.size() == CP.BitIntTypesSize);
+ }
+
+ if (Ctx.DependentBitIntTypes.size() != CP.DependentBitIntTypesSize) {
+ llvm::dbgs()
+ << "Ctx.DependentBitIntTypes.size() != CP.DependentBitIntTypesSize\n";
+ eraseFoldingSetIf(Ctx.DependentBitIntTypes,
+ [&](DependentBitIntType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentBitIntTypes.size() == CP.DependentBitIntTypesSize);
+ }
+
+ if (Ctx.BTFTagAttributedTypes.size() != CP.BTFTagAttributedTypesSize) {
+ llvm::dbgs() << "Ctx.BTFTagAttributedTypes.size() != "
+ "CP.BTFTagAttributedTypesSize\n";
+ eraseFoldingSetIf(Ctx.BTFTagAttributedTypes,
+ [&](BTFTagAttributedType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.BTFTagAttributedTypes.size() == CP.BTFTagAttributedTypesSize);
+ }
+
+ if (Ctx.HLSLAttributedResourceTypes.size() !=
+ CP.HLSLAttributedResourceTypesSize) {
+ llvm::dbgs() << "Ctx.HLSLAttributedResourceTypes.size() != "
+ "CP.HLSLAttributedResourceTypesSize\n";
+ eraseFoldingSetIf(Ctx.HLSLAttributedResourceTypes,
+ [&](HLSLAttributedResourceType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.HLSLAttributedResourceTypes.size() ==
+ CP.HLSLAttributedResourceTypesSize);
+ }
+
+ if (Ctx.HLSLInlineSpirvTypes.size() != CP.HLSLInlineSpirvTypesSize) {
+ llvm::dbgs()
+ << "Ctx.HLSLInlineSpirvTypes.size() != CP.HLSLInlineSpirvTypesSize\n";
+ eraseFoldingSetIf(Ctx.HLSLInlineSpirvTypes,
+ [&](HLSLInlineSpirvType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.HLSLInlineSpirvTypes.size() == CP.HLSLInlineSpirvTypesSize);
+ }
+
+ if (Ctx.CountAttributedTypes.size() != CP.CountAttributedTypesSize) {
+ llvm::dbgs()
+ << "Ctx.CountAttributedTypes.size() != CP.CountAttributedTypesSize\n";
+ eraseFoldingSetIf(Ctx.CountAttributedTypes,
+ [&](CountAttributedType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.CountAttributedTypes.size() == CP.CountAttributedTypesSize);
+ }
+
+ if (Ctx.QualifiedTemplateNames.size() != CP.QualifiedTemplateNamesSize) {
+ llvm::dbgs() << "Ctx.QualifiedTemplateNames.size() != "
+ "CP.QualifiedTemplateNamesSize\n";
+ eraseFoldingSetIf(Ctx.QualifiedTemplateNames,
+ [&](QualifiedTemplateName &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.QualifiedTemplateNames.size() == CP.QualifiedTemplateNamesSize);
+ }
+
+ if (Ctx.DependentTemplateNames.size() != CP.DependentTemplateNamesSize) {
+ llvm::dbgs() << "Ctx.DependentTemplateNames.size() != "
+ "CP.DependentTemplateNamesSize\n";
+ eraseFoldingSetIf(Ctx.DependentTemplateNames,
+ [&](DependentTemplateName &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DependentTemplateNames.size() == CP.DependentTemplateNamesSize);
+ }
+
+ if (Ctx.SubstTemplateTemplateParms.size() !=
+ CP.SubstTemplateTemplateParmsSize) {
+ llvm::dbgs() << "Ctx.SubstTemplateTemplateParms.size() != "
+ "CP.SubstTemplateTemplateParmsSize\n";
+ eraseFoldingSetIf(Ctx.SubstTemplateTemplateParms,
+ [&](SubstTemplateTemplateParmStorage &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.SubstTemplateTemplateParms.size() ==
+ CP.SubstTemplateTemplateParmsSize);
+ }
+
+ if (Ctx.SubstTemplateTemplateParmPacks.size() !=
+ CP.SubstTemplateTemplateParmPacksSize) {
+ llvm::dbgs() << "Ctx.SubstTemplateTemplateParmPacks.size() != "
+ "CP.SubstTemplateTemplateParmPacksSize\n";
+ eraseFoldingSetIf(Ctx.SubstTemplateTemplateParmPacks,
+ [&](SubstTemplateTemplateParmPackStorage &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.SubstTemplateTemplateParmPacks.size() ==
+ CP.SubstTemplateTemplateParmPacksSize);
+ }
+
+ if (Ctx.DeducedTemplates.size() != CP.DeducedTemplatesSize) {
+ llvm::dbgs() << "Ctx.DeducedTemplates.size() != CP.DeducedTemplatesSize\n";
+ eraseFoldingSetIf(Ctx.DeducedTemplates,
+ [&](DeducedTemplateStorage &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.DeducedTemplates.size() == CP.DeducedTemplatesSize);
+ }
+
+ if (Ctx.ArrayParameterTypes.size() != CP.ArrayParameterTypesSize) {
+ llvm::dbgs()
+ << "Ctx.ArrayParameterTypes.size() != CP.ArrayParameterTypesSize\n";
+ eraseFoldingSetIf(Ctx.ArrayParameterTypes,
+ [&](ArrayParameterType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(Ctx.ArrayParameterTypes.size() == CP.ArrayParameterTypesSize);
+ }
+
+ if (CP.PredefinedSugarTypesSize != Ctx.PredefinedSugarTypes.size()) {
+ llvm::dbgs() << "if (CP.PredefinedSugarTypesSize != "
+ "Ctx.PredefinedSugarTypes.size()\n";
+ // mutable std::array<Type *,
+ // llvm::to_underlying(PredefinedSugarType::Kind::Last) +
+ // 1>
+ // PredefinedSugarTypes{};
+ assert(CP.PredefinedSugarTypesSize == Ctx.PredefinedSugarTypes.size());
+ }
+
+ if (CP.NamespaceAndPrefixStoragesSize !=
+ Ctx.NamespaceAndPrefixStorages.size()) {
+ llvm::dbgs() << "if (CP.NamespaceAndPrefixStoragesSize != "
+ "Ctx.NamespaceAndPrefixStorages.size()\n";
+ eraseFoldingSetIf(Ctx.NamespaceAndPrefixStorages,
+ [&](NamespaceAndPrefixStorage &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(CP.NamespaceAndPrefixStoragesSize ==
+ Ctx.NamespaceAndPrefixStorages.size());
+ }
+
+ if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()) {
+ llvm::dbgs()
+ << "if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()\n";
+ eraseDenseMapIf(
+ Ctx.ASTRecordLayouts,
+ [&](llvm::detail::DenseMapPair<const RecordDecl *,
+ const ASTRecordLayout *> &KV) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(
+ const_cast<RecordDecl *>(KV.getFirst())),
+ SlabCP) ||
+ Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(
+ const_cast<ASTRecordLayout *>(KV.getSecond())),
+ SlabCP);
+ });
+ assert(CP.ASTRecordLayoutsSize == Ctx.ASTRecordLayouts.size());
+ }
+
+ if (CP.MemoizedTypeInfoSize != Ctx.MemoizedTypeInfo.size()) {
+ llvm::dbgs()
+ << "if (CP.MemoizedTypeInfoSize != Ctx.MemoizedTypeInfo.size() "
+ << CP.MemoizedTypeInfoSize << " != " << Ctx.MemoizedTypeInfo.size()
+ << " \n";
+ eraseDenseMapIf(
+ Ctx.MemoizedTypeInfo,
+ [&](llvm::detail::DenseMapPair<const Type *, struct TypeInfo> &KV)
+ -> bool {
+ // llvm::outs() << "Type * In Range : [ "
+ // << static_cast<void *>(SlabCP.CurPtr) << " < "
+ // << KV.getFirst() << " < "
+ // << static_cast<void *>(SlabCP.End) << "]\n";
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(const_cast<Type *>(KV.getFirst())), SlabCP);
+ });
+ // assert(CP.MemoizedTypeInfoSize == Ctx.MemoizedTypeInfo.size());
+ }
+
+ if (CP.MemoizedUnadjustedAlignSize != Ctx.MemoizedUnadjustedAlign.size()) {
+ llvm::dbgs() << "if (CP.MemoizedUnadjustedAlignSize != "
+ "Ctx.MemoizedUnadjustedAlign.size()\n";
+ eraseDenseMapIf(
+ Ctx.MemoizedUnadjustedAlign,
+ [&](llvm::detail::DenseMapPair<const Type *, unsigned> &KV) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(const_cast<Type *>(KV.getFirst())), SlabCP);
+ });
+ assert(CP.MemoizedUnadjustedAlignSize ==
+ Ctx.MemoizedUnadjustedAlign.size());
+ }
+
+ if (CP.KeyFunctionsSize != Ctx.KeyFunctions.size()) {
+ llvm::dbgs() << "if (CP.KeyFunctionsSize != Ctx.KeyFunctions.size()\n";
+ eraseDenseMapIf(
+ Ctx.KeyFunctions,
+ [&](llvm::detail::DenseMapPair<const CXXRecordDecl *, LazyDeclPtr> &KV)
+ -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(const_cast<CXXRecordDecl *>(KV.getFirst())),
+ SlabCP);
+ });
+ assert(CP.KeyFunctionsSize == Ctx.KeyFunctions.size());
+ }
+
+ if (CP.BlockVarCopyInitsSize != Ctx.BlockVarCopyInits.size()) {
+ llvm::dbgs()
+ << "if (CP.BlockVarCopyInitsSize != Ctx.BlockVarCopyInits.size()\n";
+ eraseDenseMapIf(
+ Ctx.BlockVarCopyInits,
+ [&](llvm::detail::DenseMapPair<const VarDecl *, BlockVarCopyInit> &KV)
+ -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(const_cast<VarDecl *>(KV.getFirst())),
+ SlabCP);
+ });
+ assert(CP.BlockVarCopyInitsSize == Ctx.BlockVarCopyInits.size());
+ }
+
+ if (CP.MSGuidDeclsSize != Ctx.MSGuidDecls.size()) {
+ llvm::dbgs() << "if (CP.MSGuidDeclsSize != Ctx.MSGuidDecls.size()\n";
+ eraseFoldingSetIf(Ctx.MSGuidDecls, [&](MSGuidDecl &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
+ SlabCP);
+ });
+ assert(CP.MSGuidDeclsSize == Ctx.MSGuidDecls.size());
+ }
+
+ if (CP.UnnamedGlobalConstantDeclsSize !=
+ Ctx.UnnamedGlobalConstantDecls.size()) {
+ llvm::dbgs() << "if (CP.UnnamedGlobalConstantDeclsSize != "
+ "Ctx.UnnamedGlobalConstantDecls.size()\n";
+ eraseFoldingSetIf(Ctx.UnnamedGlobalConstantDecls,
+ [&](UnnamedGlobalConstantDecl &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(CP.UnnamedGlobalConstantDeclsSize ==
+ Ctx.UnnamedGlobalConstantDecls.size());
+ }
+
+ if (CP.TemplateParamObjectDeclsSize != Ctx.TemplateParamObjectDecls.size()) {
+ llvm::dbgs() << "if (CP.TemplateParamObjectDeclsSize != "
+ "Ctx.TemplateParamObjectDecls.size()\n";
+ eraseFoldingSetIf(Ctx.TemplateParamObjectDecls,
+ [&](TemplateParamObjectDecl &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(CP.TemplateParamObjectDeclsSize ==
+ Ctx.TemplateParamObjectDecls.size());
+ }
+
+ if (CP.StringLiteralCacheSize != Ctx.StringLiteralCache.size()) {
+ llvm::dbgs()
+ << "if (CP.StringLiteralCacheSize != Ctx.StringLiteralCache.size()\n";
+ // mutable llvm::StringMap<StringLiteral *> StringLiteralCache;
+ assert(CP.StringLiteralCacheSize == Ctx.StringLiteralCache.size());
+ }
+
+ if (CP.DestroyingOperatorDeletesSize !=
+ Ctx.DestroyingOperatorDeletes.size()) {
+ llvm::dbgs() << "if (CP.DestroyingOperatorDeletesSize != "
+ "Ctx.DestroyingOperatorDeletes.size()\n";
+ eraseDenseSetIf(
+ Ctx.DestroyingOperatorDeletes, [&](const FunctionDecl *FD) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(const_cast<FunctionDecl *>(FD)), SlabCP);
+ });
+ assert(CP.DestroyingOperatorDeletesSize ==
+ Ctx.DestroyingOperatorDeletes.size());
+ }
+
+ if (CP.TypeAwareOperatorNewAndDeletesSize !=
+ Ctx.TypeAwareOperatorNewAndDeletes.size()) {
+ llvm::dbgs() << "CP.TypeAwareOperatorNewAndDeletesSize != "
+ "Ctx.TypeAwareOperatorNewAndDeletes.size()\n";
+ eraseDenseSetIf(Ctx.TypeAwareOperatorNewAndDeletes,
+ [&](const FunctionDecl *FD) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(const_cast<FunctionDecl *>(FD)),
+ SlabCP);
+ });
+ assert(CP.TypeAwareOperatorNewAndDeletesSize ==
+ Ctx.TypeAwareOperatorNewAndDeletes.size());
+ }
+
+ if (CP.OperatorDeletesForVirtualDtorSize !=
+ Ctx.OperatorDeletesForVirtualDtor.size()) {
+ llvm::dbgs() << "CP.OperatorDeletesForVirtualDtorSize != "
+ "Ctx.OperatorDeletesForVirtualDtor.size()\n";
+ eraseDenseMapIf(
+ Ctx.OperatorDeletesForVirtualDtor,
+ [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
+ FunctionDecl *> &KV) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(
+ const_cast<CXXDestructorDecl *>(KV.getFirst())),
+ SlabCP) ||
+ Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(KV.getSecond()), SlabCP);
+ });
+ assert(CP.OperatorDeletesForVirtualDtorSize ==
+ Ctx.OperatorDeletesForVirtualDtor.size());
+ }
+
+ if (CP.GlobalOperatorDeletesForVirtualDtorSize !=
+ Ctx.GlobalOperatorDeletesForVirtualDtor.size()) {
+ llvm::dbgs() << "CP.GlobalOperatorDeletesForVirtualDtorSize != "
+ "Ctx.GlobalOperatorDeletesForVirtualDtor.size()\n";
+ eraseDenseMapIf(
+ Ctx.GlobalOperatorDeletesForVirtualDtor,
+ [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
+ FunctionDecl *> &KV) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(
+ const_cast<CXXDestructorDecl *>(KV.getFirst())),
+ SlabCP) ||
+ Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(KV.getSecond()), SlabCP);
+ });
+ assert(CP.GlobalOperatorDeletesForVirtualDtorSize ==
+ Ctx.GlobalOperatorDeletesForVirtualDtor.size());
+ }
+
+ if (CP.ArrayOperatorDeletesForVirtualDtorSize !=
+ Ctx.ArrayOperatorDeletesForVirtualDtor.size()) {
+ llvm::dbgs() << "CP.ArrayOperatorDeletesForVirtualDtorSize != "
+ "Ctx.ArrayOperatorDeletesForVirtualDtor.size()\n";
+ eraseDenseMapIf(
+ Ctx.ArrayOperatorDeletesForVirtualDtor,
+ [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
+ FunctionDecl *> &KV) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(
+ const_cast<CXXDestructorDecl *>(KV.getFirst())),
+ SlabCP) ||
+ Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(KV.getSecond()), SlabCP);
+ });
+ assert(CP.ArrayOperatorDeletesForVirtualDtorSize ==
+ Ctx.ArrayOperatorDeletesForVirtualDtor.size());
+ }
+
+ if (CP.GlobalArrayOperatorDeletesForVirtualDtorSize !=
+ Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size()) {
+ llvm::dbgs() << "CP.GlobalArrayOperatorDeletesForVirtualDtorSize != "
+ "Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size()\n";
+ eraseDenseMapIf(
+ Ctx.GlobalArrayOperatorDeletesForVirtualDtor,
+ [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
+ FunctionDecl *> &KV) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(
+ const_cast<CXXDestructorDecl *>(KV.getFirst())),
+ SlabCP) ||
+ Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(KV.getSecond()), SlabCP);
+ });
+ assert(CP.GlobalArrayOperatorDeletesForVirtualDtorSize ==
+ Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size());
+ }
+
+ if (CP.RequireVectorDeletingDtorSize !=
+ Ctx.RequireVectorDeletingDtor.size()) {
+ llvm::dbgs() << "if (CP.RequireVectorDeletingDtorSize != "
+ "Ctx.RequireVectorDeletingDtor.size()\n";
+ eraseDenseSetIf(
+ Ctx.RequireVectorDeletingDtor, [&](const CXXRecordDecl *RD) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(const_cast<CXXRecordDecl *>(RD)), SlabCP);
+ });
+ assert(CP.RequireVectorDeletingDtorSize ==
+ Ctx.RequireVectorDeletingDtor.size());
+ }
+
+ if (CP.MergedDeclsSize != Ctx.MergedDecls.size()) {
+ llvm::dbgs() << "CP.MergedDeclsSize != Ctx.MergedDecls.size()";
+ assert(CP.MergedDeclsSize == Ctx.MergedDecls.size());
+ }
+
+ if (CP.MergedDefModulesSize != Ctx.MergedDefModules.size()) {
+ llvm::dbgs() << "CP.MergedDefModulesSize != Ctx.MergedDefModules.size()";
+ assert(CP.MergedDefModulesSize == Ctx.MergedDefModules.size());
+ }
+
+ if (CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()) {
+ llvm::dbgs()
+ << "CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()";
+ assert(CP.ModuleInitializersSize == Ctx.ModuleInitializers.size());
+ }
+
+ if (CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()) {
+ llvm::dbgs()
+ << "CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()";
+ assert(CP.PrimaryModuleNameMapSize == Ctx.PrimaryModuleNameMap.size());
+ }
+
+ if (CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()) {
+ llvm::dbgs()
+ << "CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()";
+ assert(CP.SameModuleLookupSetSize == Ctx.SameModuleLookupSet.size());
+ }
+
+ if (CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()) {
+ llvm::dbgs() << "CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()";
+ assert(CP.ScalableVecTyMapSize == Ctx.ScalableVecTyMap.size());
+ }
+
+ if (CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()) {
+ llvm::dbgs() << "CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()";
+ assert(CP.LambdaCastPathsSize == Ctx.LambdaCastPaths.size());
+ }
+
+ if (CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()) {
+ llvm::dbgs() << "CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()";
+ assert(CP.DeclRawCommentsSize == Ctx.DeclRawComments.size());
+ }
+
+ if (CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()) {
+ llvm::dbgs()
+ << "CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()";
+ assert(CP.RedeclChainCommentsSize == Ctx.RedeclChainComments.size());
+ }
+
+ if (CP.CommentlessRedeclChainsSize != Ctx.CommentlessRedeclChains.size()) {
+ llvm::dbgs() << "CP.CommentlessRedeclChainsSize != "
+ "Ctx.CommentlessRedeclChains.size()";
+ assert(CP.CommentlessRedeclChainsSize ==
+ Ctx.CommentlessRedeclChains.size());
+ }
+
+ if (CP.ParsedCommentsSize != Ctx.ParsedComments.size()) {
+ llvm::dbgs() << "CP.ParsedCommentsSize != Ctx.ParsedComments.size()";
+ assert(CP.ParsedCommentsSize == Ctx.ParsedComments.size());
+ }
+
+ if (CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()) {
+ llvm::dbgs()
+ << "CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()";
+ assert(CP.RelocatableClassesSize == Ctx.RelocatableClasses.size());
+ }
+
+ if (CP.ParamIndicesSize != Ctx.ParamIndices.size()) {
+ llvm::dbgs() << "CP.ParamIndicesSize != Ctx.ParamIndices.size()";
+ assert(CP.ParamIndicesSize == Ctx.ParamIndices.size());
+ }
+
+ if (CP.MangleNumbersSize != Ctx.MangleNumbers.size()) {
+ llvm::dbgs() << "CP.MangleNumbersSize != Ctx.MangleNumbers.size()";
+ assert(CP.MangleNumbersSize == Ctx.MangleNumbers.size());
+ }
+
+ if (CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()) {
+ llvm::dbgs()
+ << "CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()";
+ assert(CP.StaticLocalNumbersSize == Ctx.StaticLocalNumbers.size());
+ }
+
+ if (CP.TemplateOrInstantiationSize != Ctx.TemplateOrInstantiation.size()) {
+ llvm::dbgs() << "CP.TemplateOrInstantiationSize != "
+ "Ctx.TemplateOrInstantiation.size()";
+ assert(CP.TemplateOrInstantiationSize ==
+ Ctx.TemplateOrInstantiation.size());
+ }
+
+ if (CP.InstantiatedFromUsingDeclSize !=
+ Ctx.InstantiatedFromUsingDecl.size()) {
+ llvm::dbgs() << "CP.InstantiatedFromUsingDeclSize != "
+ "Ctx.InstantiatedFromUsingDecl.size()";
+ assert(CP.InstantiatedFromUsingDeclSize ==
+ Ctx.InstantiatedFromUsingDecl.size());
+ }
+
+ if (CP.InstantiatedFromUsingEnumDeclSize !=
+ Ctx.InstantiatedFromUsingEnumDecl.size()) {
+ llvm::dbgs() << "CP.InstantiatedFromUsingEnumDeclSize != "
+ "Ctx.InstantiatedFromUsingEnumDecl.size()";
+ assert(CP.InstantiatedFromUsingEnumDeclSize ==
+ Ctx.InstantiatedFromUsingEnumDecl.size());
+ }
+
+ if (CP.InstantiatedFromUsingShadowDeclSize !=
+ Ctx.InstantiatedFromUsingShadowDecl.size()) {
+ llvm::dbgs() << "CP.InstantiatedFromUsingShadowDeclSize != "
+ "Ctx.InstantiatedFromUsingShadowDecl.size()";
+ assert(CP.InstantiatedFromUsingShadowDeclSize ==
+ Ctx.InstantiatedFromUsingShadowDecl.size());
+ }
+
+ if (CP.InstantiatedFromUnnamedFieldDeclSize !=
+ Ctx.InstantiatedFromUnnamedFieldDecl.size()) {
+ llvm::dbgs() << "CP.InstantiatedFromUnnamedFieldDeclSize != "
+ "Ctx.InstantiatedFromUnnamedFieldDecl.size()";
+ assert(CP.InstantiatedFromUnnamedFieldDeclSize ==
+ Ctx.InstantiatedFromUnnamedFieldDecl.size());
+ }
+
+ if (CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()) {
+ llvm::dbgs() << "CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()";
+ assert(CP.OverriddenMethodsSize == Ctx.OverriddenMethods.size());
+ }
+
+ if (CP.MangleNumberingContextsSize != Ctx.MangleNumberingContexts.size()) {
+ llvm::dbgs() << "CP.MangleNumberingContextsSize != "
+ "Ctx.MangleNumberingContexts.size()";
+ assert(CP.MangleNumberingContextsSize ==
+ Ctx.MangleNumberingContexts.size());
+ }
+
+ if (CP.ExtraMangleNumberingContextsSize !=
+ Ctx.ExtraMangleNumberingContexts.size()) {
+ llvm::dbgs() << "CP.ExtraMangleNumberingContextsSize != "
+ "Ctx.ExtraMangleNumberingContexts.size()";
+ assert(CP.ExtraMangleNumberingContextsSize ==
+ Ctx.ExtraMangleNumberingContexts.size());
+ }
+
+ if (CP.TraversalScopeSize != Ctx.TraversalScope.size()) {
+ llvm::dbgs() << "CP.TraversalScopeSize != "
+ "Ctx.TraversalScope.size()";
+ assert(CP.TraversalScopeSize == Ctx.TraversalScope.size());
+ }
+
+ for (auto &[Decl, OldTy] : Ctx.PendingTypeForDeclMutations)
+ Decl->TypeForDecl = OldTy;
+
+ Ctx.PendingTypeForDeclMutations.clear();
+ TranslationUnitDecl *MostRecentTU = Ctx.getTranslationUnitDecl();
+ for (const auto *DC : Ctx.PendingDCMutations) {
+ if (MostRecentTU->getPrimaryContext() == DC)
+ continue;
+ if (StoredDeclsMap *Map = const_cast<DeclContext *>(DC)
+ ->getPrimaryContext()
+ ->getLookupPtr()) {
+ for (auto &&[Key, List] : *Map) {
+ DeclContextLookupResult R = List.getLookupResult();
+ std::vector<NamedDecl *> NamedDeclsToRemove;
+ // bool RemoveAll = true;
+ for (NamedDecl *D : R) {
+ // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU (" << (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
+ // llvm::outs() << "DeclContext : " << DC << "\n";
+ // D->dump();
+ // if (D->getTranslationUnitDecl() == MostRecentTU)
+ if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D),
+ SlabCP))
+ NamedDeclsToRemove.push_back(D);
+ // else
+ // RemoveAll = false;
+ }
+ // if (LLVM_LIKELY(RemoveAll)) {
+ // Map->erase(Key);
+ // } else {
+ for (NamedDecl *D : NamedDeclsToRemove)
+ List.remove(D);
+ // }
+ }
+ }
+
+ Decl *Prev = DC->FirstDecl;
+ Decl *Cur = DC->FirstDecl;
+ while (Cur) {
+ if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
+ SlabCP)) {
+ DC->LastDecl = Prev;
+ DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
+ break;
+ }
+ Prev = Cur;
+ Cur = Cur->getNextDeclInContext();
+ }
+ }
+
+ // if (FirstDecl) {
+ // LastDecl->NextInContextAndBits.setPointer(D);
+ // LastDecl = D;
+ // } else {
+ // FirstDecl = LastDecl = D;
+ // }
+
+ // Notify a C++ record declaration that we've added a member, so it can
+ // update its class-specific state.
+ // if (auto *Record = dyn_cast<CXXRecordDecl>(this))
+ // Record->addedMember(D);
+
+
+ // ImportDecl *FirstLocalImport = nullptr;
+ // ImportDecl *LastLocalImport = nullptr;
+
+
+ // class ImportDecl final : public Decl,
+ // llvm::TrailingObjects<ImportDecl, SourceLocation> {
+ // friend class ASTContext;
+ // friend class ASTDeclReader;
+ // friend class ASTReader;
+ // friend TrailingObjects;
+
+ // /// The imported module.
+ // Module *ImportedModule = nullptr;
+
+ // /// The next import in the list of imports local to the translation
+ // /// unit being parsed (not loaded from an AST file).
+ // ///
+ // /// Includes a bit that indicates whether we have source-location information
+ // /// for each identifier in the module name.
+ // ///
+ // /// When the bit is false, we only have a single source location for the
+ // /// end of the import declaration.
+ // llvm::PointerIntPair<ImportDecl *, 1, bool> NextLocalImportAndComplete;
+
+ Ctx.PendingDCMutations.clear();
+
+ llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM = Ctx.LastSDM;
+
+ StoredDeclsMap *Map = LastSDM.getPointer();
+ bool Dependent = LastSDM.getInt();
+ while (Map && (Map != CP.LastSDM.getPointer())) {
+ // llvm::outs() << "we are deleting LASTSDM\n";
+ // // Advance the iteration before we invalidate memory.
+ llvm::PointerIntPair<StoredDeclsMap*,1> Next = Map->Previous;
+
+ if (Dependent)
+ delete static_cast<DependentStoredDeclsMap*>(Map);
+ else
+ delete Map;
+
+ Map = Next.getPointer();
+ Dependent = Next.getInt();
+ }
+
+ Ctx.LastSDM = CP.LastSDM;
+
+ Ctx.Types.resize(CP.TypesSize);
+}
+
+void ASTContextStateStash::commit() {
+ Ctx.PendingTypeForDeclMutations.clear();
+ Ctx.PendingDCMutations.clear();
+}
+} // end namespace clang
\ No newline at end of file
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index 01d3295d1ac30..ab8e418e8d731 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -20,12 +20,14 @@ if (EMSCRIPTEN AND "lld" IN_LIST LLVM_ENABLE_PROJECTS)
endif()
add_clang_library(clangInterpreter
+ ASTContextStateStash.cpp
DeviceOffload.cpp
CodeCompletion.cpp
IncrementalAction.cpp
IncrementalExecutor.cpp
OrcIncrementalExecutor.cpp
IncrementalParser.cpp
+ SemaStateStash.cpp
Interpreter.cpp
InterpreterValuePrinter.cpp
InterpreterUtils.cpp
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index 3cfabb5e5daa1..bdd9d41b0ce48 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -329,6 +329,8 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
// Lookup alone is not enough: the redeclaration chain still reaches these.
withdrawMostRecentTU(MostRecentTU);
+ RepairRedeclChain(MostRecentTU, MostRecentTU);
+ S.getASTContext().setTranslationUnitDecl(MostRecentTU->getPreviousDecl());
}
PartialTranslationUnit &
diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h
index b626cebaafcd7..e6eaa7dddb7fd 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -63,6 +63,9 @@ class IncrementalParser {
void CleanUpPTU(TranslationUnitDecl *MostRecentTU);
+ template <typename decl_type>
+ void RepairRedeclChain(decl_type *D, TranslationUnitDecl *PTU);
+
/// Register a PTU produced by Parse.
PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU,
std::unique_ptr<llvm::Module> M = {});
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 655db32477a0c..f951425c9c862 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -35,6 +35,7 @@
#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
#include "clang/FrontendTool/Utils.h"
+#include "clang/Interpreter/ErrorRecovery.h"
#include "clang/Interpreter/IncrementalExecutor.h"
#include "clang/Interpreter/Interpreter.h"
#include "clang/Interpreter/Value.h"
@@ -567,6 +568,39 @@ size_t Interpreter::getEffectivePTUSize() const {
llvm::Expected<PartialTranslationUnit &>
Interpreter::Parse(llvm::StringRef Code) {
+ class PTUSlabRollback {
+ public:
+ explicit PTUSlabRollback(Sema &S)
+ : Ctx(S.getASTContext()), ASTCtxState(Ctx), SemaState(S),
+ CheckPoint(Ctx.getAllocator().checkPoint()) {
+ SemaState.stash(SemaCheckPoint);
+ ASTCtxState.stash(CtxCheckPoint);
+ }
+
+ ~PTUSlabRollback() {
+ if (!Committed) {
+ SemaState.restore(SemaCheckPoint, CheckPoint);
+ ASTCtxState.restore(CtxCheckPoint, CheckPoint);
+ Ctx.getAllocator().restoreToCheckPoint(CheckPoint);
+ }
+ }
+
+ void commit(PartialTranslationUnit &PTU) {
+ ASTCtxState.commit();
+ PTU.SlabCheckPoint = CheckPoint;
+ Committed = true;
+ }
+
+ private:
+ ASTContext &Ctx;
+ ASTContextStateStash ASTCtxState;
+ SemaStateStash SemaState;
+ llvm::SlabCheckPoint CheckPoint;
+ bool Committed = false;
+ StashCheckPoint CtxCheckPoint;
+ SemaStashCheckPoint SemaCheckPoint;
+ };
+
// If we have a device parser, parse it first. The generated code will be
// included in the host compilation
if (DeviceParser) {
@@ -590,6 +624,8 @@ Interpreter::Parse(llvm::StringRef Code) {
getCompilerInstance()->getDiagnostics().setSeverity(
clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation());
+ PTUSlabRollback Rollback(CI->getSema());
+
llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
if (!TuOrErr)
return TuOrErr.takeError();
@@ -602,6 +638,7 @@ Interpreter::Parse(llvm::StringRef Code) {
frontend::EmitLLVM)
LastPTU.TheModule->print(llvm::outs(), /*AAW=*/nullptr);
+ Rollback.commit(LastPTU);
return LastPTU;
}
diff --git a/clang/lib/Interpreter/SemaStateStash.cpp b/clang/lib/Interpreter/SemaStateStash.cpp
new file mode 100644
index 0000000000000..8dce1182c4279
--- /dev/null
+++ b/clang/lib/Interpreter/SemaStateStash.cpp
@@ -0,0 +1,882 @@
+//===--- SemaStateStash.cpp - Sema persistent state stash/restore
+//----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Interpreter/ErrorRecovery.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaInternal.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang {
+
+// template <typename EntryType, typename PredT>
+// static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred)
+// {
+// SmallVector<EntryType *, 16> ToRemove;
+// for (auto &N : FS)
+// if (Pred(N))
+// ToRemove.push_back(&N);
+// for (auto *N : ToRemove)
+// FS.RemoveNode(N);
+// }
+
+// template <typename EntryType, typename PredT>
+// static void eraseContextualFoldingSetIf(
+// llvm::ContextualFoldingSet<EntryType, const ASTContext &> &FS,
+// PredT &&Pred) {
+// SmallVector<EntryType *, 16> ToRemove;
+// for (auto &N : FS)
+// if (Pred(N))
+// ToRemove.push_back(&N);
+// for (auto *N : ToRemove)
+// FS.RemoveNode(N);
+// }
+
+/// Erase from DenseMap based on predicate
+template <typename KeyT, typename ValueT, typename PredT>
+static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
+ SmallVector<KeyT, 16> ToRemove;
+
+ for (auto &KV : Map)
+ if (Pred(KV))
+ ToRemove.push_back(KV.getFirst());
+
+ for (auto &Key : ToRemove)
+ Map.erase(Key);
+}
+
+// template <typename ValueT, typename PredT>
+// static void eraseDenseSetIf(llvm::DenseSet<ValueT> &Set, PredT &&Pred) {
+// SmallVector<ValueT, 16> ToRemove;
+// for (const auto &Val : Set)
+// if (Pred(Val))
+// ToRemove.push_back(Val);
+// for (const auto &Val : ToRemove)
+// Set.erase(Val);
+// }
+
+// template <typename T, typename PredT>
+// static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, 4> &Set, PredT &&Pred) {
+// SmallVector<T, 8> ToRemove;
+// for (T Val : Set)
+// if (Pred(Val))
+// ToRemove.push_back(Val);
+// for (T Val : ToRemove)
+// Set.erase(Val);
+// }
+
+// template <typename T, unsigned N, typename PredT>
+// static void eraseSmallSetVectorIf(llvm::SmallSetVector<T, N> &SV,
+// PredT &&Pred) {
+// SmallVector<T, 8> ToRemove;
+// for (const T &Val : SV)
+// if (Pred(Val))
+// ToRemove.push_back(Val);
+// for (const T &Val : ToRemove)
+// SV.remove(Val);
+// }
+
+// template <typename KeyT, typename ValueT, typename PredT>
+// static void eraseMapVectorIf(llvm::MapVector<KeyT, ValueT> &MV, PredT &&Pred)
+// {
+// SmallVector<KeyT, 16> ToRemove;
+// for (auto &KV : MV)
+// if (Pred(KV))
+// ToRemove.push_back(KV.first);
+// for (const auto &Key : ToRemove)
+// MV.erase(Key);
+// }
+
+// template <typename T, typename PredT>
+// static void eraseVectorIf(SmallVectorImpl<T> &Vec, PredT &&Pred) {
+// llvm::erase_if(Vec, std::forward<PredT>(Pred));
+// }
+
+//===----------------------------------------------------------------------===//
+// Pragma / value snapshot (uses Sema friend access for nested types)
+//===----------------------------------------------------------------------===//
+
+// struct SemaStateStash::PragmaSnapshot {
+// Sema::PragmaClangSection PragmaClangBSSSection;
+// Sema::PragmaClangSection PragmaClangDataSection;
+// Sema::PragmaClangSection PragmaClangRodataSection;
+// Sema::PragmaClangSection PragmaClangRelroSection;
+// Sema::PragmaClangSection PragmaClangTextSection;
+
+// Sema::PragmaStack<MSVtorDispMode> VtorDispStack;
+// Sema::PragmaStack<Sema::AlignPackInfo> AlignPackStack;
+// Sema::PragmaStack<StringLiteral *> DataSegStack;
+// Sema::PragmaStack<StringLiteral *> BSSSegStack;
+// Sema::PragmaStack<StringLiteral *> ConstSegStack;
+// Sema::PragmaStack<StringLiteral *> CodeSegStack;
+// Sema::PragmaStack<bool> StrictGuardStackCheckStack;
+// Sema::PragmaStack<FPOptionsOverride> FpPragmaStack;
+
+// StringLiteral *CurInitSeg = nullptr;
+// SourceLocation CurInitSegLoc;
+// bool MSPragmaOptimizeIsOn = true;
+// SourceLocation OptimizeOffPragmaLocation;
+
+// FileNullabilityMap NullabilityMap;
+// };
+
+void SemaStateStash::stash(SemaStashCheckPoint &CP) {
+ CP.SemaBumpSlabCP = S.BumpAlloc.checkPoint();
+ // CP.CachedFunctionScopeSize = S.CachedFunctionScope.size();
+ CP.FunctionScopesSize = S.FunctionScopes.size();
+
+ // CP.Ident_superSize = S.Ident_super.size();
+
+ /// --- Pragma ---
+ // CP.PragmaClangBSSSectionSize = S.PragmaClangBSSSection.size();
+ // CP.PragmaClangDataSectionSize = S.PragmaClangDataSection.size();
+ // CP.PragmaClangRodataSectionSize = S.PragmaClangRodataSection.size();
+ // CP.PragmaClangRelroSectionSize = S.PragmaClangRelroSection.size();
+ // CP.PragmaClangTextSectionSize = S.PragmaClangTextSection.size();
+ // CP.VtorDispStackSize = S.VtorDispStack.size();
+ // CP.AlignPackStackSize = S.AlignPackStack.size();
+ // CP.AlignPackIncludeStackSize = S.AlignPackIncludeStack.size();
+ // CP.DataSegStackSize = S.DataSegStack.size();
+ // CP.BSSSegStackSize = S.BSSSegStack.size();
+ // CP.ConstSegStackSize = S.ConstSegStack.size();
+ // CP.CodeSegStackSize = S.CodeSegStack.size();
+ // CP.StrictGuardStackCheckStackSize = S.StrictGuardStackCheckStack.size();
+ // CP.FpPragmaStackSize = S.FpPragmaStack.size();
+
+ // CP.FunctionToSectionMapSize = S.FunctionToSectionMap.size();
+ // CP.PragmaAttributeStackSize = S.PragmaAttributeStack.size();
+ // CP.MSFunctionNoBuiltinsSize = S.MSFunctionNoBuiltins.size();
+ // CP.PendingExportedNamesSize = S.PendingExportedNames.size();
+ CP.TypoCorrectedFunctionDefinitionsSize =
+ S.TypoCorrectedFunctionDefinitions.size();
+ CP.FlagBitsCacheSize = S.FlagBitsCache.size();
+ CP.AssignEnumCacheSize = S.AssignEnumCache.size();
+ CP.WeakUndeclaredIdentifiersSize = S.WeakUndeclaredIdentifiers.size();
+ CP.ExtnameUndeclaredIdentifiersSize = S.ExtnameUndeclaredIdentifiers.size();
+ CP.UnusedLocalTypedefNameCandidatesSize =
+ S.UnusedLocalTypedefNameCandidates.size();
+ CP.UnusedFileScopedDeclsSize = S.UnusedFileScopedDecls.end();
+ CP.TentativeDefinitionsSize = S.TentativeDefinitions.end();
+ CP.ExternalDeclarationsSize = S.ExternalDeclarations.size();
+ CP.ParsingInitForAutoVarsSize = S.ParsingInitForAutoVars.size();
+ CP.DeclsToCheckForDeferredDiagsSize = S.DeclsToCheckForDeferredDiags.size();
+ CP.ShadowingDeclsSize = S.ShadowingDecls.size();
+ CP.WeakTopLevelDeclSize = S.WeakTopLevelDecl.size();
+ CP.ExtVectorDeclsSize = S.ExtVectorDecls.end();
+ CP.VTableUsesSize = S.VTableUses.size();
+ CP.VTablesUsedSize = S.VTablesUsed.size();
+ CP.DelayedDllExportClassesSize = S.DelayedDllExportClasses.size();
+ CP.DelayedDllExportMemberFunctionsSize =
+ S.DelayedDllExportMemberFunctions.size();
+ CP.InventedParameterInfosSize = S.InventedParameterInfos.size();
+ // CP.FieldCollectorSize = S.FieldCollector.size();
+ CP.UnusedPrivateFieldsSize = S.UnusedPrivateFields.size();
+ if (S.PureVirtualClassDiagSet)
+ CP.PureVirtualClassDiagSetSize = S.PureVirtualClassDiagSet->size();
+ CP.DelegatingCtorDeclsSize = S.DelegatingCtorDecls.end();
+ // CP.StdNamespaceSize = S.StdNamespace.size();
+ CP.UnparsedDefaultArgLocsSize = S.UnparsedDefaultArgLocs.size();
+ CP.UndefinedButUsedSize = S.UndefinedButUsed.size();
+ CP.SpecialMembersBeingDeclaredSize = S.SpecialMembersBeingDeclared.size();
+ CP.DelayedOverridingExceptionSpecChecksSize =
+ S.DelayedOverridingExceptionSpecChecks.size();
+ CP.DelayedEquivalentExceptionSpecChecksSize =
+ S.DelayedEquivalentExceptionSpecChecks.size();
+ CP.MaybeODRUseExprsSize = S.MaybeODRUseExprs.size();
+ CP.RefsMinusAssignmentsSize = S.RefsMinusAssignments.size();
+ CP.ExprCleanupObjectsSize = S.ExprCleanupObjects.size();
+ CP.ExprEvalContextsSize = S.ExprEvalContexts.size();
+ CP.FailedImmediateInvocationsSize = S.FailedImmediateInvocations.size();
+ // CP.ImplicitlyRetainedSelfLocsSize = S.ImplicitlyRetainedSelfLocs.size();
+ CP.DeleteExprsSize = S.DeleteExprs.size();
+ CP.CurrentParameterCopyTypesSize = S.CurrentParameterCopyTypes.size();
+ CP.AggregateDeductionCandidatesSize = S.AggregateDeductionCandidates.size();
+ CP.TypoCorrectionFailuresSize = S.TypoCorrectionFailures.size();
+ CP.SpecialMemberCacheSize = S.SpecialMemberCache.size();
+ CP.ModuleScopesSize = S.ModuleScopes.size();
+ CP.DeferredExportedNamespacesSize = S.DeferredExportedNamespaces.size();
+ CP.PendingInlineFuncDeclsSize = S.PendingInlineFuncDecls.size();
+ CP.CurrentSEHFinallySize = S.CurrentSEHFinally.size();
+ CP.CurrentDeferSize = S.CurrentDefer.size();
+ CP.LateParsedTemplateMapSize = S.LateParsedTemplateMap.size();
+ CP.SuppressedDiagnosticsSize = S.SuppressedDiagnostics.size();
+ // CP.CurrentInstantiationScopeSize = S.CurrentInstantiationScope.size();
+ CP.UnparsedDefaultArgInstantiationsSize =
+ S.UnparsedDefaultArgInstantiations.size();
+ CP.CodeSynthesisContextsSize = S.CodeSynthesisContexts.size();
+ CP.InstantiatingSpecializationsSize = S.InstantiatingSpecializations.size();
+ CP.InstantiatedNonDependentTypesSize = S.InstantiatedNonDependentTypes.size();
+ CP.CodeSynthesisContextLookupModulesSize =
+ S.CodeSynthesisContextLookupModules.size();
+ CP.LookupModulesCacheSize = S.LookupModulesCache.size();
+ CP.VisibleNamespaceCacheSize = S.VisibleNamespaceCache.size();
+ CP.TemplateInstCallbacksSize = S.TemplateInstCallbacks.size();
+ CP.PendingInstantiationsSize = S.PendingInstantiations.size();
+ CP.LateParsedInstantiationsSize = S.LateParsedInstantiations.size();
+ CP.SavedVTableUsesSize = S.SavedVTableUses.size();
+ CP.SavedPendingInstantiationsSize = S.SavedPendingInstantiations.size();
+ CP.PendingLocalImplicitInstantiationsSize =
+ S.PendingLocalImplicitInstantiations.size();
+ CP.UnsubstitutedConstraintSatisfactionCacheSize =
+ S.UnsubstitutedConstraintSatisfactionCache.size();
+ CP.SubsumptionCacheSize = S.SubsumptionCache.size();
+ CP.NormalizationCacheSize = S.NormalizationCache.size();
+ CP.SatisfactionCacheSize = S.SatisfactionCache.size();
+ CP.SatisfactionStackSize = S.SatisfactionStack.size();
+ // CP.NullabilityMapSize = S.NullabilityMap.size();
+ CP.DeclsWithEffectsToVerifySize = S.DeclsWithEffectsToVerify.size();
+ // CP.AllEffectsToVerifySize = S.AllEffectsToVerify.size();
+}
+
+void SemaStateStash::restore(SemaStashCheckPoint &CP,
+ llvm::SlabCheckPoint SlabCP) {
+
+ ASTContext &Ctx = S.getASTContext();
+ if (CP.FunctionScopesSize != S.FunctionScopes.size()) {
+ llvm::dbgs() << "CP.FunctionScopesSize != S.FunctionScopes.size()\n";
+ S.FunctionScopes.resize(CP.FunctionScopesSize);
+ assert(CP.FunctionScopesSize == S.FunctionScopes.size());
+ }
+
+ if (CP.TypoCorrectedFunctionDefinitionsSize !=
+ S.TypoCorrectedFunctionDefinitions.size()) {
+ llvm::dbgs() << "CP.TypoCorrectedFunctionDefinitionsSize != "
+ "S.TypoCorrectedFunctionDefinitions.size()\n";
+ // llvm::SmallPtrSet<const NamedDecl *, 4> TypoCorrectedFunctionDefinitions;
+ assert(CP.TypoCorrectedFunctionDefinitionsSize ==
+ S.TypoCorrectedFunctionDefinitions.size());
+ }
+
+ if (CP.FlagBitsCacheSize != S.FlagBitsCache.size()) {
+ llvm::dbgs() << "CP.FlagBitsCacheSize != S.FlagBitsCache.size()\n";
+ // mutable llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache;
+ assert(CP.FlagBitsCacheSize == S.FlagBitsCache.size());
+ }
+
+ if (CP.AssignEnumCacheSize != S.AssignEnumCache.size()) {
+ llvm::dbgs() << "CP.AssignEnumCacheSize != S.AssignEnumCache.size()\n";
+ // llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt>>
+ // AssignEnumCache;
+ assert(CP.AssignEnumCacheSize == S.AssignEnumCache.size());
+ }
+
+ if (CP.WeakUndeclaredIdentifiersSize != S.WeakUndeclaredIdentifiers.size()) {
+ llvm::dbgs() << "CP.WeakUndeclaredIdentifiersSize != "
+ "S.WeakUndeclaredIdentifiers.size()\n";
+ // llvm::MapVector<
+ // IdentifierInfo *,
+ // llvm::SetVector<
+ // WeakInfo, llvm::SmallVector<WeakInfo, 1u>,
+ // llvm::SmallDenseSet<WeakInfo, 2u,
+ // WeakInfo::DenseMapInfoByAliasOnly>>>
+ // WeakUndeclaredIdentifiers;
+ assert(CP.WeakUndeclaredIdentifiersSize ==
+ S.WeakUndeclaredIdentifiers.size());
+ }
+
+ if (CP.ExtnameUndeclaredIdentifiersSize !=
+ S.ExtnameUndeclaredIdentifiers.size()) {
+ llvm::dbgs() << "CP.ExtnameUndeclaredIdentifiersSize != "
+ "S.ExtnameUndeclaredIdentifiers.size()\n";
+ // llvm::DenseMap<IdentifierInfo *, AsmLabelAttr *>
+ // ExtnameUndeclaredIdentifiers;
+ assert(CP.ExtnameUndeclaredIdentifiersSize ==
+ S.ExtnameUndeclaredIdentifiers.size());
+ }
+
+ if (CP.UnusedLocalTypedefNameCandidatesSize !=
+ S.UnusedLocalTypedefNameCandidates.size()) {
+ llvm::dbgs() << "CP.UnusedLocalTypedefNameCandidatesSize != "
+ "S.UnusedLocalTypedefNameCandidates.size()\n";
+ // llvm::SmallSetVector<const TypedefNameDecl *, 4>
+ // UnusedLocalTypedefNameCandidates;
+ assert(CP.UnusedLocalTypedefNameCandidatesSize ==
+ S.UnusedLocalTypedefNameCandidates.size());
+ }
+
+ if (CP.UnusedFileScopedDeclsSize != S.UnusedFileScopedDecls.end()) {
+ llvm::dbgs()
+ << "CP.UnusedFileScopedDeclsSize != S.UnusedFileScopedDecls.end()\n";
+ // typedef LazyVector<const DeclaratorDecl *, ExternalSemaSource,
+ // &ExternalSemaSource::ReadUnusedFileScopedDecls, 2, 2>
+ // UnusedFileScopedDeclsType;
+ assert(CP.UnusedFileScopedDeclsSize == S.UnusedFileScopedDecls.end());
+ }
+
+ if (CP.TentativeDefinitionsSize != S.TentativeDefinitions.end()) {
+ llvm::dbgs()
+ << "CP.TentativeDefinitionsSize != S.TentativeDefinitions.size()\n";
+ // typedef LazyVector<VarDecl *, ExternalSemaSource,
+ // &ExternalSemaSource::ReadTentativeDefinitions, 2, 2>
+ // TentativeDefinitionsType;
+ assert(CP.TentativeDefinitionsSize == S.TentativeDefinitions.end());
+ }
+
+ if (CP.ExternalDeclarationsSize != S.ExternalDeclarations.size()) {
+ llvm::dbgs()
+ << "CP.ExternalDeclarationsSize != S.ExternalDeclarations.size()\n";
+ // SmallVector<DeclaratorDecl *, 4> ExternalDeclarations;
+ assert(CP.ExternalDeclarationsSize == S.ExternalDeclarations.size());
+ }
+
+ if (CP.ParsingInitForAutoVarsSize != S.ParsingInitForAutoVars.size()) {
+ llvm::dbgs()
+ << "CP.ParsingInitForAutoVarsSize != S.ParsingInitForAutoVars.size()\n";
+ // llvm::SmallPtrSet<const Decl *, 4> ParsingInitForAutoVars;
+ assert(CP.ParsingInitForAutoVarsSize == S.ParsingInitForAutoVars.size());
+ }
+
+ if (CP.DeclsToCheckForDeferredDiagsSize !=
+ S.DeclsToCheckForDeferredDiags.size()) {
+ llvm::dbgs() << "CP.DeclsToCheckForDeferredDiagsSize != "
+ "S.DeclsToCheckForDeferredDiags.size()\n";
+ // llvm::SmallSetVector<Decl *, 4> DeclsToCheckForDeferredDiags;
+ assert(CP.DeclsToCheckForDeferredDiagsSize ==
+ S.DeclsToCheckForDeferredDiags.size());
+ }
+
+ if (CP.ShadowingDeclsSize != S.ShadowingDecls.size()) {
+ llvm::dbgs() << "CP.ShadowingDeclsSize != S.ShadowingDecls.size()\n";
+ // llvm::DenseMap<const NamedDecl *, const NamedDecl *> ShadowingDecls;
+ assert(CP.ShadowingDeclsSize == S.ShadowingDecls.size());
+ }
+
+ if (CP.WeakTopLevelDeclSize != S.WeakTopLevelDecl.size()) {
+ llvm::dbgs() << "CP.WeakTopLevelDeclSize != S.WeakTopLevelDecl.size()\n";
+ // SmallVector<Decl *, 2> WeakTopLevelDecl;
+ assert(CP.WeakTopLevelDeclSize == S.WeakTopLevelDecl.size());
+ }
+
+ if (CP.ExtVectorDeclsSize != S.ExtVectorDecls.end()) {
+ llvm::dbgs() << "CP.ExtVectorDeclsSize != S.ExtVectorDecls.size()\n";
+ // typedef LazyVector<TypedefNameDecl *, ExternalSemaSource,
+ // &ExternalSemaSource::ReadExtVectorDecls, 2, 2> ExtVectorDecls;
+ assert(CP.ExtVectorDeclsSize == S.ExtVectorDecls.end());
+ }
+
+ if (CP.VTableUsesSize != S.VTableUses.size()) {
+ llvm::dbgs() << "CP.VTableUsesSize != S.VTableUses.size()\n";
+ // SmallVector<VTableUse, 16> VTableUses;
+ assert(CP.VTableUsesSize == S.VTableUses.size());
+ }
+
+ if (CP.VTablesUsedSize != S.VTablesUsed.size()) {
+ llvm::dbgs() << "CP.VTablesUsedSize != S.VTablesUsed.size()\n";
+ // llvm::DenseMap<CXXRecordDecl *, bool> VTablesUsed;
+ assert(CP.VTablesUsedSize == S.VTablesUsed.size());
+ }
+
+ if (CP.DelayedDllExportClassesSize != S.DelayedDllExportClasses.size()) {
+ llvm::dbgs() << "CP.DelayedDllExportClassesSize != "
+ "S.DelayedDllExportClasses.size()\n";
+ // SmallVector<CXXRecordDecl *, 4> DelayedDllExportClasses;
+ assert(CP.DelayedDllExportClassesSize == S.DelayedDllExportClasses.size());
+ }
+
+ if (CP.DelayedDllExportMemberFunctionsSize !=
+ S.DelayedDllExportMemberFunctions.size()) {
+ llvm::dbgs() << "CP.DelayedDllExportMemberFunctionsSize != "
+ "S.DelayedDllExportMemberFunctions.size()\n";
+ // SmallVector<CXXMethodDecl *, 4> DelayedDllExportMemberFunctions;
+ assert(CP.DelayedDllExportMemberFunctionsSize ==
+ S.DelayedDllExportMemberFunctions.size());
+ }
+
+ if (CP.InventedParameterInfosSize != S.InventedParameterInfos.size()) {
+ llvm::dbgs()
+ << "CP.InventedParameterInfosSize != S.InventedParameterInfos.size()\n";
+ // SmallVector<InventedTemplateParameterInfo, 4> InventedParameterInfos;
+ assert(CP.InventedParameterInfosSize == S.InventedParameterInfos.size());
+ }
+
+ if (CP.UnusedPrivateFieldsSize != S.UnusedPrivateFields.size()) {
+ llvm::dbgs()
+ << "CP.UnusedPrivateFieldsSize != S.UnusedPrivateFields.size()\n";
+ // typedef llvm::SmallSetVector<const NamedDecl *, 16> NamedDeclSetType;
+ /// Set containing all declared private fields that are not used.
+ // NamedDeclSetType UnusedPrivateFields;
+ assert(CP.UnusedPrivateFieldsSize == S.UnusedPrivateFields.size());
+ }
+
+ if (S.PureVirtualClassDiagSet &&
+ (CP.PureVirtualClassDiagSetSize != S.PureVirtualClassDiagSet->size())) {
+ llvm::dbgs() << "CP.PureVirtualClassDiagSetSize != "
+ "S.PureVirtualClassDiagSet.size()\n";
+ // typedef llvm::SmallPtrSet<const CXXRecordDecl *, 8> RecordDeclSetTy;
+
+ /// PureVirtualClassDiagSet - a set of class declarations which we have
+ /// emitted a list of pure virtual functions. Used to prevent emitting the
+ /// same list more than once.
+ // std::unique_ptr<RecordDeclSetTy> PureVirtualClassDiagSet;
+ assert(CP.PureVirtualClassDiagSetSize == S.PureVirtualClassDiagSet->size());
+ }
+
+ if (CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.end()) {
+ llvm::dbgs()
+ << "CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.size()\n";
+ // typedef LazyVector<CXXConstructorDecl *, ExternalSemaSource,
+ // &ExternalSemaSource::ReadDelegatingConstructors, 2, 2>
+ // DelegatingCtorDeclsType;
+
+ /// All the delegating constructors seen so far in the file, used for
+ /// cycle detection at the end of the TU.
+ // DelegatingCtorDeclsType DelegatingCtorDecls;
+
+ assert(CP.DelegatingCtorDeclsSize == S.DelegatingCtorDecls.end());
+ }
+
+ if (CP.UnparsedDefaultArgLocsSize != S.UnparsedDefaultArgLocs.size()) {
+ llvm::dbgs()
+ << "CP.UnparsedDefaultArgLocsSize != S.UnparsedDefaultArgLocs.size()\n";
+ // llvm::DenseMap<ParmVarDecl *, SourceLocation> UnparsedDefaultArgLocs;
+ assert(CP.UnparsedDefaultArgLocsSize == S.UnparsedDefaultArgLocs.size());
+ }
+
+ if (CP.UndefinedButUsedSize != S.UndefinedButUsed.size()) {
+ llvm::dbgs() << "CP.UndefinedButUsedSize != S.UndefinedButUsed.size()\n";
+ // llvm::MapVector<NamedDecl *, SourceLocation> UndefinedButUsed;
+ while (CP.UndefinedButUsedSize != S.UndefinedButUsed.size())
+ S.UndefinedButUsed.pop_back();
+ assert(CP.UndefinedButUsedSize == S.UndefinedButUsed.size());
+ }
+
+ if (CP.SpecialMembersBeingDeclaredSize !=
+ S.SpecialMembersBeingDeclared.size()) {
+ llvm::dbgs() << "CP.SpecialMembersBeingDeclaredSize != "
+ "S.SpecialMembersBeingDeclared.size()\n";
+ // llvm::SmallPtrSet<SpecialMemberDecl, 4> SpecialMembersBeingDeclared;
+ assert(CP.SpecialMembersBeingDeclaredSize ==
+ S.SpecialMembersBeingDeclared.size());
+ }
+
+ if (CP.SpecialMembersBeingDeclaredSize !=
+ S.SpecialMembersBeingDeclared.size()) {
+ llvm::dbgs() << "CP.SpecialMembersBeingDeclaredSize != "
+ "S.SpecialMembersBeingDeclared.size()\n";
+ // llvm::SmallPtrSet<SpecialMemberDecl, 4> SpecialMembersBeingDeclared;
+ assert(CP.SpecialMembersBeingDeclaredSize ==
+ S.SpecialMembersBeingDeclared.size());
+ }
+
+ if (CP.DelayedOverridingExceptionSpecChecksSize !=
+ S.DelayedOverridingExceptionSpecChecks.size()) {
+ llvm::dbgs() << "CP.DelayedOverridingExceptionSpecChecksSize != "
+ "S.DelayedOverridingExceptionSpecChecks.size()\n";
+ // SmallVector<std::pair<const CXXMethodDecl *, const CXXMethodDecl *>,
+ // 2>
+ // DelayedOverridingExceptionSpecChecks;
+ assert(CP.DelayedOverridingExceptionSpecChecksSize ==
+ S.DelayedOverridingExceptionSpecChecks.size());
+ }
+
+ if (CP.DelayedEquivalentExceptionSpecChecksSize !=
+ S.DelayedEquivalentExceptionSpecChecks.size()) {
+ llvm::dbgs() << "CP.DelayedEquivalentExceptionSpecChecksSize != "
+ "S.DelayedEquivalentExceptionSpecChecks.size()\n";
+ // SmallVector<std::pair<FunctionDecl *, FunctionDecl *>, 2>
+ // DelayedEquivalentExceptionSpecChecks;
+ assert(CP.DelayedEquivalentExceptionSpecChecksSize ==
+ S.DelayedEquivalentExceptionSpecChecks.size());
+ }
+
+ if (CP.MaybeODRUseExprsSize != S.MaybeODRUseExprs.size()) {
+ llvm::dbgs() << "CP.MaybeODRUseExprsSize != S.MaybeODRUseExprs.size()\n";
+ // S.MaybeODRUseExprs.resize(CP.MaybeODRUseExprsSize);
+ assert(CP.MaybeODRUseExprsSize == S.MaybeODRUseExprs.size());
+ }
+
+ if (CP.RefsMinusAssignmentsSize != S.RefsMinusAssignments.size()) {
+ llvm::dbgs()
+ << "CP.RefsMinusAssignmentsSize != S.RefsMinusAssignments.size()\n";
+ // llvm::DenseMap<const VarDecl *, int> RefsMinusAssignments;
+ eraseDenseMapIf(
+ S.RefsMinusAssignments,
+ [&](llvm::detail::DenseMapPair<const VarDecl *, int> &KV) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(const_cast<VarDecl *>(KV.getFirst())),
+ SlabCP);
+ });
+ assert(CP.RefsMinusAssignmentsSize == S.RefsMinusAssignments.size());
+ }
+
+ if (CP.ExprCleanupObjectsSize != S.ExprCleanupObjects.size()) {
+ llvm::dbgs()
+ << "CP.ExprCleanupObjectsSize != S.ExprCleanupObjects.size()\n";
+ // SmallVector<ExprWithCleanups::CleanupObject, 8> ExprCleanupObjects;
+ assert(CP.ExprCleanupObjectsSize == S.ExprCleanupObjects.size());
+ }
+
+ if (CP.ExprEvalContextsSize != S.ExprEvalContexts.size()) {
+ llvm::dbgs() << "CP.ExprEvalContextsSize != S.ExprEvalContexts.size()\n";
+ // SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts;
+ assert(CP.ExprEvalContextsSize == S.ExprEvalContexts.size());
+ }
+
+ if (CP.FailedImmediateInvocationsSize !=
+ S.FailedImmediateInvocations.size()) {
+ llvm::dbgs() << "CP.FailedImmediateInvocationsSize != "
+ "S.FailedImmediateInvocations.size()\n";
+ // llvm::SmallPtrSet<ConstantExpr *, 4> FailedImmediateInvocations;
+ assert(CP.FailedImmediateInvocationsSize ==
+ S.FailedImmediateInvocations.size());
+ }
+
+ if (CP.DeleteExprsSize != S.DeleteExprs.size()) {
+ llvm::dbgs() << "CP.DeleteExprsSize != S.DeleteExprs.size()\n";
+ // llvm::MapVector<FieldDecl *, DeleteLocs> DeleteExprs;
+ assert(CP.DeleteExprsSize == S.DeleteExprs.size());
+ }
+
+ if (CP.CurrentParameterCopyTypesSize != S.CurrentParameterCopyTypes.size()) {
+ llvm::dbgs() << "CP.CurrentParameterCopyTypesSize != "
+ "S.CurrentParameterCopyTypes.size()\n";
+ // llvm::SmallVector<QualType, 4> CurrentParameterCopyTypes;
+ assert(CP.CurrentParameterCopyTypesSize ==
+ S.CurrentParameterCopyTypes.size());
+ }
+
+ if (CP.AggregateDeductionCandidatesSize !=
+ S.AggregateDeductionCandidates.size()) {
+ llvm::dbgs() << "CP.AggregateDeductionCandidatesSize != "
+ "S.AggregateDeductionCandidates.size()\n";
+ // llvm::DenseMap<unsigned, CXXDeductionGuideDecl *>
+ // AggregateDeductionCandidates;
+ assert(CP.AggregateDeductionCandidatesSize ==
+ S.AggregateDeductionCandidates.size());
+ }
+
+ if (CP.TypoCorrectionFailuresSize != S.TypoCorrectionFailures.size()) {
+ llvm::dbgs() << "CP.TypoCorrectionFailuresSize != "
+ "S.TypoCorrectionFailures.size()\n";
+ // eraseDenseMapIf(
+ // S.TypoCorrectionFailures,
+ // [&](llvm::detail::DenseMapPair<IdentifierInfo *, Sema::SrcLocSet> &KV)
+ // -> bool {
+ // llvm::outs() << "SlabCheckPoint Cur : " << (void *)SlabCP.CurPtr << "\n";
+ // llvm::outs() << "SlabCheckPoint END : " << (void *)SlabCP.End << "\n";
+ // llvm::outs() << "Addr: " << static_cast<void *>(KV.getFirst()) << "\n";
+ // return Ctx.getAllocator().isAfterCheckpoint(
+ // static_cast<void *>(KV.getFirst()), SlabCP);
+ // });
+ // assert(CP.TypoCorrectionFailuresSize == S.TypoCorrectionFailures.size());
+ }
+
+ if (CP.ModuleScopesSize != S.ModuleScopes.size()) {
+ llvm::dbgs() << "CP.ModuleScopesSize != "
+ "S.ModuleScopes.size()\n";
+ // llvm::SmallVector<ModuleScope, 16> ModuleScopes;
+ assert(CP.ModuleScopesSize == S.ModuleScopes.size());
+ }
+
+ if (CP.DeferredExportedNamespacesSize !=
+ S.DeferredExportedNamespaces.size()) {
+ llvm::dbgs() << "CP.DeferredExportedNamespacesSize != "
+ "S.DeferredExportedNamespaces.size()\n";
+ /// Namespace definitions that we will export when they finish.
+ // llvm::SmallPtrSet<const NamespaceDecl *, 8> DeferredExportedNamespaces;
+ assert(CP.DeferredExportedNamespacesSize ==
+ S.DeferredExportedNamespaces.size());
+ }
+
+ if (CP.PendingInlineFuncDeclsSize != S.PendingInlineFuncDecls.size()) {
+ llvm::dbgs() << "CP.PendingInlineFuncDeclsSize != "
+ "S.PendingInlineFuncDecls.size()\n";
+ /// /// In a C++ standard module, inline declarations require a definition
+ /// to be
+ /// present at the end of a definition domain. This set holds the decls to
+ /// be checked at the end of the TU.
+ // llvm::SmallPtrSet<const FunctionDecl *, 8> PendingInlineFuncDecls;
+
+ assert(CP.PendingInlineFuncDeclsSize == S.PendingInlineFuncDecls.size());
+ }
+
+ if (CP.CurrentSEHFinallySize != S.CurrentSEHFinally.size()) {
+ llvm::dbgs() << "CP.CurrentSEHFinallySize != "
+ "S.CurrentSEHFinally.size()\n";
+ // /// Stack of active SEH __finally scopes. Can be empty.
+ // SmallVector<Scope *, 2> CurrentSEHFinally;
+
+ assert(CP.CurrentSEHFinallySize == S.CurrentSEHFinally.size());
+ }
+
+ if (CP.CurrentDeferSize != S.CurrentDefer.size()) {
+ llvm::dbgs() << "CP.CurrentDeferSize != "
+ "S.CurrentDefer.size()\n";
+ // /// Stack of '_Defer' statements that are currently being parsed, as
+ // well
+ /// as the locations of their '_Defer' keywords. Can be empty.
+ // SmallVector<std::pair<Scope *, SourceLocation>, 2> CurrentDefer;
+
+ assert(CP.CurrentDeferSize == S.CurrentDefer.size());
+ }
+
+ if (CP.LateParsedTemplateMapSize != S.LateParsedTemplateMap.size()) {
+ llvm::dbgs() << "CP.LateParsedTemplateMapSize != "
+ "S.LateParsedTemplateMap.size()\n";
+ // typedef llvm::MapVector<const FunctionDecl *,
+ // std::unique_ptr<LateParsedTemplate>>
+ // LateParsedTemplateMapT;
+ // LateParsedTemplateMapT LateParsedTemplateMap;
+ assert(CP.LateParsedTemplateMapSize == S.LateParsedTemplateMap.size());
+ }
+
+ if (CP.SuppressedDiagnosticsSize != S.SuppressedDiagnostics.size()) {
+ llvm::dbgs() << "CP.SuppressedDiagnosticsSize != "
+ "S.SuppressedDiagnostics.size()\n";
+ // typedef llvm::DenseMap<Decl *, SmallVector<PartialDiagnosticAt, 1>>
+ // SuppressedDiagnosticsMap;
+ // SuppressedDiagnosticsMap SuppressedDiagnostics;
+ assert(CP.SuppressedDiagnosticsSize == S.SuppressedDiagnostics.size());
+ }
+
+ if (CP.UnparsedDefaultArgInstantiationsSize !=
+ S.UnparsedDefaultArgInstantiations.size()) {
+ llvm::dbgs() << "CP.UnparsedDefaultArgInstantiationsSize != "
+ "S.UnparsedDefaultArgInstantiations.size()\n";
+
+ // typedef llvm::DenseMap<ParmVarDecl *, llvm::TinyPtrVector<ParmVarDecl
+ // *>>
+ // UnparsedDefaultArgInstantiationsMap;
+
+ // /// A mapping from parameters with unparsed default arguments to the
+ // /// set of instantiations of each parameter.
+ // ///
+ // /// This mapping is a temporary data structure used when parsing
+ // /// nested class templates or nested classes of class templates,
+ // /// where we might end up instantiating an inner class before the
+ // /// default arguments of its methods have been parsed.
+ // UnparsedDefaultArgInstantiationsMap UnparsedDefaultArgInstantiations;
+
+ assert(CP.UnparsedDefaultArgInstantiationsSize ==
+ S.UnparsedDefaultArgInstantiations.size());
+ }
+
+ if (CP.CodeSynthesisContextsSize != S.CodeSynthesisContexts.size()) {
+ llvm::dbgs() << "CP.CodeSynthesisContextsSize != "
+ "S.CodeSynthesisContexts.size()\n";
+ // SmallVector<CodeSynthesisContext, 16> CodeSynthesisContexts;
+
+ assert(CP.CodeSynthesisContextsSize == S.CodeSynthesisContexts.size());
+ }
+
+ if (CP.InstantiatingSpecializationsSize !=
+ S.InstantiatingSpecializations.size()) {
+ llvm::dbgs() << "CP.InstantiatingSpecializationsSize != "
+ "S.InstantiatingSpecializations.size()\n";
+ // /// Specializations whose definitions are currently being
+ // instantiated.
+ // llvm::DenseSet<InstantiatingSpecializationsKey>
+ // InstantiatingSpecializations;
+
+ assert(CP.InstantiatingSpecializationsSize ==
+ S.InstantiatingSpecializations.size());
+ }
+
+ if (CP.InstantiatedNonDependentTypesSize !=
+ S.InstantiatedNonDependentTypes.size()) {
+ llvm::dbgs() << "CP.InstantiatedNonDependentTypesSize != "
+ "S.InstantiatedNonDependentTypes.size()\n";
+ // llvm::DenseSet<QualType> InstantiatedNonDependentTypes;
+ assert(CP.InstantiatedNonDependentTypesSize ==
+ S.InstantiatedNonDependentTypes.size());
+ }
+
+ if (CP.CodeSynthesisContextLookupModulesSize !=
+ S.CodeSynthesisContextLookupModules.size()) {
+ llvm::dbgs() << "CP.CodeSynthesisContextLookupModulesSize != "
+ "S.CodeSynthesisContextLookupModules.size()\n";
+ // SmallVector<Module *, 16> CodeSynthesisContextLookupModules;
+
+ assert(CP.CodeSynthesisContextLookupModulesSize ==
+ S.CodeSynthesisContextLookupModules.size());
+ }
+
+ if (CP.LookupModulesCacheSize != S.LookupModulesCache.size()) {
+ llvm::dbgs() << "CP.LookupModulesCacheSize != "
+ "S.LookupModulesCache.size()\n";
+ // llvm::DenseSet<Module *> LookupModulesCache;
+
+ assert(CP.LookupModulesCacheSize == S.LookupModulesCache.size());
+ }
+
+ if (CP.VisibleNamespaceCacheSize != S.VisibleNamespaceCache.size()) {
+ llvm::dbgs() << "CP.VisibleNamespaceCacheSize != "
+ "S.VisibleNamespaceCache.size()\n";
+ // llvm::DenseMap<NamedDecl *, NamedDecl *> VisibleNamespaceCache;
+
+ assert(CP.VisibleNamespaceCacheSize == S.VisibleNamespaceCache.size());
+ }
+
+ if (CP.TemplateInstCallbacksSize != S.TemplateInstCallbacks.size()) {
+ llvm::dbgs() << "CP.TemplateInstCallbacksSize != "
+ "S.TemplateInstCallbacks.size()\n";
+ // std::vector<std::unique_ptr<TemplateInstantiationCallback>>
+ // TemplateInstCallbacks;
+ assert(CP.TemplateInstCallbacksSize == S.TemplateInstCallbacks.size());
+ }
+
+ if (CP.PendingInstantiationsSize != S.PendingInstantiations.size()) {
+ llvm::dbgs() << "CP.PendingInstantiationsSize != "
+ "S.PendingInstantiations.size()\n";
+ // std::deque<PendingImplicitInstantiation> PendingInstantiations;
+
+ assert(CP.PendingInstantiationsSize == S.PendingInstantiations.size());
+ }
+
+ if (CP.LateParsedInstantiationsSize != S.LateParsedInstantiations.size()) {
+ llvm::dbgs() << "CP.LateParsedInstantiationsSize != "
+ "S.LateParsedInstantiations.size()\n";
+ S.LateParsedInstantiations.resize(CP.LateParsedInstantiationsSize);
+ assert(CP.LateParsedInstantiationsSize ==
+ S.LateParsedInstantiations.size());
+ }
+
+ if (CP.SavedVTableUsesSize != S.SavedVTableUses.size()) {
+ llvm::dbgs() << "CP.SavedVTableUsesSize != "
+ "S.SavedVTableUses.size()\n";
+ S.SavedVTableUses.resize(CP.SavedVTableUsesSize);
+ assert(CP.SavedVTableUsesSize == S.SavedVTableUses.size());
+ }
+
+ if (CP.SavedPendingInstantiationsSize !=
+ S.SavedPendingInstantiations.size()) {
+ llvm::dbgs() << "CP.SavedPendingInstantiationsSize != "
+ "S.SavedPendingInstantiations.size()\n";
+ S.SavedPendingInstantiations.resize(CP.SavedPendingInstantiationsSize);
+ assert(CP.SavedPendingInstantiationsSize ==
+ S.SavedPendingInstantiations.size());
+ }
+
+ if (CP.PendingLocalImplicitInstantiationsSize !=
+ S.PendingLocalImplicitInstantiations.size()) {
+ llvm::dbgs() << "CP.PendingLocalImplicitInstantiationsSize != "
+ "S.PendingLocalImplicitInstantiations.size()\n";
+ S.PendingLocalImplicitInstantiations.resize(
+ CP.PendingLocalImplicitInstantiationsSize);
+ assert(CP.PendingLocalImplicitInstantiationsSize ==
+ S.PendingLocalImplicitInstantiations.size());
+ }
+
+ if (CP.UnsubstitutedConstraintSatisfactionCacheSize !=
+ S.UnsubstitutedConstraintSatisfactionCache.size()) {
+ llvm::dbgs() << "CP.UnsubstitutedConstraintSatisfactionCacheSize != "
+ "S.UnsubstitutedConstraintSatisfactionCache.size()\n";
+ // eraseDenseMapIf(
+ // S.UnsubstitutedConstraintSatisfactionCache,
+ // [&](UnsubstitutedConstraintSatisfactionCacheResult &R) -> bool {
+ // return Ctx.isAfterCheckpoint(static_cast<void
+ // *>(R.SubstExpr.get()),
+ // SlabCP)
+ // });
+ assert(CP.UnsubstitutedConstraintSatisfactionCacheSize ==
+ S.UnsubstitutedConstraintSatisfactionCache.size());
+ }
+
+ if (CP.SubsumptionCacheSize != S.SubsumptionCache.size()) {
+ llvm::dbgs() << "CP.SubsumptionCacheSize != "
+ "S.SubsumptionCache.size()\n";
+ // eraseDenseMapIf(
+ // S.SubsumptionCache,
+ // [&](llvm::DenseMap<std::pair<const NamedDecl *, const NamedDecl *>,
+ // bool>::key_value &KV) -> bool {
+ // return Ctx.isAfterCheckpoint(static_cast<void *>(KV.first.first),
+ // SlabCP) ||
+ // Ctx.isAfterCheckpoint(static_cast<void *>(KV.first.second),
+ // SlabCP);
+ // });
+ assert(CP.SubsumptionCacheSize == S.SubsumptionCache.size());
+ }
+
+ if (CP.NormalizationCacheSize != S.NormalizationCache.size()) {
+ llvm::dbgs() << "CP.NormalizationCacheSize != "
+ "S.NormalizationCache.size()\n";
+ // llvm::DenseMap<ConstrainedDeclOrNestedRequirement, NormalizedConstraint
+ // *> NormalizationCache;
+ assert(CP.NormalizationCacheSize == S.NormalizationCache.size());
+ }
+
+ if (CP.SatisfactionCacheSize != S.SatisfactionCache.size()) {
+ llvm::dbgs() << "CP.SatisfactionCacheSize != "
+ "S.SatisfactionCache.size()\n";
+ // llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &>
+ // SatisfactionCache;
+ assert(CP.SatisfactionCacheSize == S.SatisfactionCache.size());
+ }
+
+ if (CP.SatisfactionStackSize != S.SatisfactionStack.size()) {
+ llvm::dbgs() << "CP.SatisfactionStackSize != "
+ "S.SatisfactionStack.size()\n";
+ // llvm::SmallVector<SatisfactionStackEntryTy, 10>
+ // SatisfactionStack;
+ assert(CP.SatisfactionStackSize == S.SatisfactionStack.size());
+ }
+
+ // if (CP.NullabilityMapSize != S.NullabilityMap.size()) {
+ // llvm::dbgs() << "CP.NullabilityMapSize != "
+ // "S.NullabilityMap.size()\n";
+ // // FileNullabilityMap NullabilityMap;;
+ // assert(CP.NullabilityMapSize == S.NullabilityMap.size());
+ // }
+
+ if (CP.DeclsWithEffectsToVerifySize != S.DeclsWithEffectsToVerify.size()) {
+ llvm::dbgs() << "CP.DeclsWithEffectsToVerifySize != "
+ "S.DeclsWithEffectsToVerify.size()\n";
+ // SmallVector<const Decl *> DeclsWithEffectsToVerify;;
+ assert(CP.DeclsWithEffectsToVerifySize ==
+ S.DeclsWithEffectsToVerify.size());
+ }
+
+ if (CP.SpecialMemberCacheSize != S.SpecialMemberCache.size()) {
+ llvm::dbgs() << "CP.SpecialMemberCacheSize != "
+ "S.SpecialMemberCache.size()\n";
+ // eraseFoldingSetIf(S.SpecialMemberCache,
+ // [&](Sema::SpecialMemberOverloadResultEntry &Node) ->
+ // bool {
+ // return S.BumpAlloc.isAfterCheckpoint(
+ // static_cast<void *>(&Node), CP.SemaBumpSlabCP);
+ // });
+ assert(CP.SpecialMemberCacheSize == S.SpecialMemberCache.size());
+ }
+
+ std::vector<NamedDecl *> ToRemoved;
+ for (auto *TmpD : S.getCurScope()->decls()) {
+ assert(TmpD && "This decl didn't get pushed??");
+
+ assert(isa<NamedDecl>(TmpD) && "Decl isn't NamedDecl?");
+ NamedDecl *D = cast<NamedDecl>(TmpD);
+
+ if (!D->getDeclName()) continue;
+
+ if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D), SlabCP)) {
+ if (D->getDeclName().getFETokenInfo())
+ S.IdResolver.RemoveDecl(D);
+ ToRemoved.push_back(D);
+ }
+ }
+
+ for (auto ND : ToRemoved)
+ S.getCurScope()->RemoveDecl(ND);
+
+ S.BumpAlloc.restoreToCheckPoint(CP.SemaBumpSlabCP);
+}
+
+} // namespace clang
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index 79e2040fcd381..3709972ea99d8 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -23,6 +23,7 @@
#include "llvm/Support/AllocatorBase.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
@@ -41,6 +42,13 @@ LLVM_ABI void printBumpPtrAllocatorStats(unsigned NumSlabs, size_t TotalMemory);
} // end namespace detail
+struct SlabCheckPoint {
+ unsigned ActiveSlabIdx;
+ char *CurPtr;
+ char *End;
+ size_t BytesAllocated;
+};
+
/// Allocate memory in an ever growing pool, as if by bump-pointer.
///
/// This isn't strictly a bump-pointer allocator as it uses backing slabs of
@@ -209,8 +217,16 @@ class BumpPtrAllocatorImpl
return AlignedPtr;
}
- // Otherwise, start a new slab and try again.
- StartNewSlab();
+ if (!Slabs.empty() && Slabs.size() > 1 &&
+ (ActiveSlabIdx < Slabs.size() - 1)) {
+ ActiveSlabIdx++;
+ void *NewSlab = Slabs[ActiveSlabIdx];
+ size_t AllocatedSlabSize = computeSlabSize(ActiveSlabIdx);
+ CurPtr = (char *)(NewSlab);
+ End = ((char *)NewSlab) + AllocatedSlabSize;
+ } else
+ // Otherwise, start a new slab and try again.
+ StartNewSlab();
uintptr_t AlignedAddr = alignAddr(CurPtr, Alignment);
assert(AlignedAddr + SizeToAllocate < EndSentinel &&
"Unable to allocate memory!");
@@ -242,6 +258,61 @@ class BumpPtrAllocatorImpl
size_t GetNumSlabs() const { return Slabs.size() + CustomSizedSlabs.size(); }
+ SlabCheckPoint checkPoint() const {
+ return {ActiveSlabIdx, CurPtr, End, BytesAllocated};
+ }
+
+ static void poisonMemory(void *Ptr, size_t Size) {
+#if LLVM_ADDRESS_SANITIZER_BUILD
+ __asan_poison_memory_region(Ptr, Size);
+#else
+ // In non-ASAN builds, overwrite with a known poison pattern
+ // so use-after-rewind crashes deterministically in debug builds
+// #ifndef NDEBUG
+ memset(Ptr, 0xCD, Size); // 0xCD = classic "dead memory" pattern
+// #endif
+#endif
+ }
+
+ bool isAfterCheckpoint(const void *Ptr, const SlabCheckPoint &CP) const {
+ const char *P = static_cast<const char *>(Ptr);
+
+ // Check active slab — past the checkpoint CurPtr
+ if (CP.ActiveSlabIdx < Slabs.size()) {
+ const char *Start = static_cast<const char *>(Slabs[CP.ActiveSlabIdx]);
+ if (P >= CP.CurPtr && P < (Start + computeSlabSize(CP.ActiveSlabIdx)))
+ return true;
+ }
+
+ // Check slabs allocated entirely after checkpoint
+ for (unsigned I = CP.ActiveSlabIdx + 1; I < Slabs.size(); ++I) {
+ const char *Start = static_cast<const char *>(Slabs[I]);
+ const char *End = Start + computeSlabSize(I);
+ if (P >= Start && P < End)
+ return true;
+ }
+
+ return false;
+ }
+
+ void restoreToCheckPoint(SlabCheckPoint CP) {
+ assert(CP.ActiveSlabIdx >= 0 && CP.ActiveSlabIdx < Slabs.size());
+ assert(CP.CurPtr >= (const char *)Slabs[CP.ActiveSlabIdx] &&
+ CP.End == ((const char *)Slabs[CP.ActiveSlabIdx] +
+ computeSlabSize(CP.ActiveSlabIdx)));
+ ActiveSlabIdx = CP.ActiveSlabIdx;
+ CurPtr = CP.CurPtr;
+ End = CP.End;
+ BytesAllocated = CP.BytesAllocated;
+ llvm::outs() << "Poisoned range = [" << (void *)CurPtr << ", " << (void *)End << ")\n";
+ llvm::outs() << "Poisoned End Size = [" << (void *)CurPtr << ", " << (void *)(CurPtr + (size_t)(End - CurPtr)) << ")\n";
+ llvm::outs().flush();
+ poisonMemory((void *)CurPtr, (size_t)(End - CurPtr));
+ for (unsigned I = ActiveSlabIdx + 1; I < Slabs.size(); ++I)
+ // Should we deallocate any extra slabs?
+ poisonMemory(Slabs[I], computeSlabSize(I));
+ }
+
/// \return An index uniquely and reproducibly identifying
/// an input pointer \p Ptr in the given allocator.
/// The returned value is negative iff the object is inside a custom-size
@@ -325,6 +396,8 @@ class BumpPtrAllocatorImpl
/// path condition also rejects a empty allocator with a 0-size allocation.
uintptr_t EndSentinel = 0;
+ unsigned ActiveSlabIdx = 0;
+
/// The slabs allocated so far.
SmallVector<void *, 4> Slabs;
@@ -349,7 +422,8 @@ class BumpPtrAllocatorImpl
/// Allocate a new slab and move the bump pointers over into the new
/// slab, modifying CurPtr and EndSentinel.
void StartNewSlab() {
- size_t AllocatedSlabSize = computeSlabSize(Slabs.size());
+ ActiveSlabIdx = Slabs.size();
+ size_t AllocatedSlabSize = computeSlabSize(ActiveSlabIdx);
void *NewSlab = this->getAllocator().Allocate(AllocatedSlabSize,
alignof(std::max_align_t));
>From fd752aabad6ac6a4fdf7e2a4635298d60bbe1595 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 26 Jul 2026 12:37:31 +0530
Subject: [PATCH 02/13] Add fixes for crashes found during testing
---
clang/include/clang/AST/DeclBase.h | 2 +
clang/include/clang/AST/RecordLayout.h | 2 +
.../include/clang/Interpreter/ErrorRecovery.h | 23 +-
.../lib/Interpreter/ASTContextStateStash.cpp | 285 +++++++++++++-----
clang/lib/Interpreter/IncrementalAction.cpp | 12 +-
clang/lib/Interpreter/IncrementalAction.h | 3 +-
clang/lib/Interpreter/IncrementalParser.cpp | 1 +
clang/lib/Interpreter/Interpreter.cpp | 4 +-
clang/lib/Interpreter/SemaStateStash.cpp | 66 ++--
.../ptu-adl-namespace-collisions.cpp | 97 ++++++
.../test/Interpreter/ptu-class-edge-cases.cpp | 105 +++++++
.../Interpreter/ptu-class-sema-recovery.cpp | 112 +++++++
.../test/Interpreter/ptu-lookup-recovery.cpp | 49 +++
clang/test/Interpreter/ptu-rewind-stress.cpp | 259 ++++++++++++++++
14 files changed, 914 insertions(+), 106 deletions(-)
create mode 100644 clang/test/Interpreter/ptu-adl-namespace-collisions.cpp
create mode 100644 clang/test/Interpreter/ptu-class-edge-cases.cpp
create mode 100644 clang/test/Interpreter/ptu-class-sema-recovery.cpp
create mode 100644 clang/test/Interpreter/ptu-lookup-recovery.cpp
create mode 100644 clang/test/Interpreter/ptu-rewind-stress.cpp
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 39082a8973b2b..9ec6f4c84a857 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -1345,9 +1345,11 @@ namespace llvm {
}
namespace clang {
+class ASTContextStateStash;
/// A list storing NamedDecls in the lookup tables.
class DeclListNode {
friend class ASTContext; // allocate, deallocate nodes.
+ friend class ASTContextStateStash;
friend class StoredDeclsList;
public:
using Decls = llvm::PointerUnion<NamedDecl*, DeclListNode*>;
diff --git a/clang/include/clang/AST/RecordLayout.h b/clang/include/clang/AST/RecordLayout.h
index 9af17df229b37..73ecb90fbcd88 100644
--- a/clang/include/clang/AST/RecordLayout.h
+++ b/clang/include/clang/AST/RecordLayout.h
@@ -26,6 +26,7 @@
namespace clang {
class ASTContext;
+class ASTContextStateStash;
class CXXRecordDecl;
/// ASTRecordLayout -
@@ -60,6 +61,7 @@ class ASTRecordLayout {
private:
friend class ASTContext;
+ friend class ASTContextStateStash;
/// Size - Size of record in characters.
CharUnits Size;
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 6d5605a9624e6..245d9416a8933 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -188,8 +188,6 @@ struct StashCheckPoint {
size_t TypedefTypesSize = 0;
size_t DependentNameTypesSize = 0;
size_t PackExpansionTypesSize = 0;
- size_t ObjCObjectTypesSize = 0;
- size_t ObjCObjectPointerTypesSize = 0;
size_t UnaryTransformTypesSize = 0;
size_t AutoTypesSize = 0;
@@ -248,6 +246,7 @@ struct StashCheckPoint {
size_t RequireVectorDeletingDtorSize = 0;
size_t MergedDeclsSize = 0;
+ size_t DeclAttrsSize = 0;
size_t MergedDefModulesSize = 0;
size_t ModuleInitializersSize = 0;
@@ -277,8 +276,25 @@ struct StashCheckPoint {
size_t ExtraMangleNumberingContextsSize = 0;
size_t TraversalScopeSize = 0;
- llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
+
+ /// object-c
+ size_t ObjCObjectTypesSize = 0;
+ size_t ObjCObjectPointerTypesSize = 0;
+
+ mutable TypedefDecl *ObjCIdDeclCP = nullptr;
+
+ mutable TypedefDecl *ObjCSelDeclCP = nullptr;
+
+ mutable TypedefDecl *ObjCClassDeclCP = nullptr;
+
+ mutable ObjCInterfaceDecl *ObjCProtocolClassDeclCP = nullptr;
+
+ // llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
// = llvm::PointerIntPair<StoredDeclsMap *, 1>(nullptr, 0);
+ mutable QualType AutoDeductTy; // Deduction against 'auto'.
+ mutable QualType AutoRRefDeductTy; // Deduction against 'auto &&'.
+
+ // mutable DeclarationNameTable DeclarationNames; need to revert.
};
class ASTContextStateStash {
@@ -295,6 +311,5 @@ class ASTContextStateStash {
void restore(StashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
void commit();
};
-
} // end namespace clang
#endif // LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
diff --git a/clang/lib/Interpreter/ASTContextStateStash.cpp b/clang/lib/Interpreter/ASTContextStateStash.cpp
index 18ad727ddc5ef..e25c67c46aa01 100644
--- a/clang/lib/Interpreter/ASTContextStateStash.cpp
+++ b/clang/lib/Interpreter/ASTContextStateStash.cpp
@@ -11,11 +11,13 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
-#include "clang/AST/DeclContextInternals.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclContextInternals.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/MangleNumberingContext.h"
+#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
@@ -30,7 +32,6 @@ namespace clang {
template <typename EntryType, typename PredT>
static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
SmallVector<EntryType *, 16> ToRemove;
-
for (auto &N : FS)
if (Pred(N))
ToRemove.push_back(&N);
@@ -57,7 +58,6 @@ eraseFoldingSetIf(llvm::ContextualFoldingSet<EntryType, ASTContext &> &FS,
template <typename KeyT, typename ValueT, typename PredT>
static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
SmallVector<KeyT, 16> ToRemove;
-
for (auto &KV : Map)
if (Pred(KV))
ToRemove.push_back(KV.getFirst());
@@ -191,6 +191,7 @@ void ASTContextStateStash::stash(StashCheckPoint &CP) {
CP.RequireVectorDeletingDtorSize = Ctx.RequireVectorDeletingDtor.size();
CP.MergedDeclsSize = Ctx.MergedDecls.size();
+ CP.DeclAttrsSize = Ctx.DeclAttrs.size();
CP.MergedDefModulesSize = Ctx.MergedDefModules.size();
CP.ModuleInitializersSize = Ctx.ModuleInitializers.size();
@@ -222,11 +223,33 @@ void ASTContextStateStash::stash(StashCheckPoint &CP) {
CP.MangleNumberingContextsSize = Ctx.MangleNumberingContexts.size();
CP.ExtraMangleNumberingContextsSize = Ctx.ExtraMangleNumberingContexts.size();
CP.TraversalScopeSize = Ctx.TraversalScope.size();
- CP.LastSDM = Ctx.LastSDM;
+ // CP.LastSDM = Ctx.LastSDM;
+ CP.AutoDeductTy = Ctx.AutoDeductTy; // Deduction against 'auto'.
+ CP.AutoRRefDeductTy = Ctx.AutoRRefDeductTy; // Deduction against 'auto &&'.
+
+ /// ObjectC
+ CP.ObjCObjectTypesSize = Ctx.ObjCObjectTypes.size();
+ CP.ObjCObjectPointerTypesSize = Ctx.ObjCObjectPointerTypes.size();
+ CP.ObjCIdDeclCP = Ctx.ObjCIdDecl;
+
+ CP.ObjCSelDeclCP = Ctx.ObjCSelDecl;
+
+ CP.ObjCClassDeclCP = Ctx.ObjCClassDecl;
+
+ Ctx.ObjCProtocolClassDecl = CP.ObjCProtocolClassDeclCP;
}
void ASTContextStateStash::restore(StashCheckPoint &CP,
llvm::SlabCheckPoint SlabCP) {
+ if (CP.AutoDeductTy != Ctx.AutoDeductTy) {
+ llvm::dbgs() << "Ctx.AutoDeductTy != CP.AutoDeductTy\n";
+ Ctx.AutoDeductTy = CP.AutoDeductTy;
+ }
+
+ if (CP.AutoRRefDeductTy != Ctx.AutoRRefDeductTy) {
+ llvm::dbgs() << "Ctx.AutoRRefDeductTy != CP.AutoRRefDeductTy\n";
+ Ctx.AutoRRefDeductTy = CP.AutoRRefDeductTy;
+ }
if (Ctx.TemplateTypeParmTypes.size() != CP.TemplateTypeParmTypesSize) {
llvm::dbgs() << "Ctx.TemplateTypeParmTypes.size() != "
@@ -864,18 +887,22 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()) {
llvm::dbgs()
<< "if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()\n";
+ std::vector<const ASTRecordLayout *> ToBeDestroyed;
eraseDenseMapIf(
Ctx.ASTRecordLayouts,
[&](llvm::detail::DenseMapPair<const RecordDecl *,
const ASTRecordLayout *> &KV) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(
- const_cast<RecordDecl *>(KV.getFirst())),
- SlabCP) ||
- Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(
- const_cast<ASTRecordLayout *>(KV.getSecond())),
- SlabCP);
+ bool IsAfter =
+ Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(const_cast<RecordDecl *>(KV.getFirst())),
+ SlabCP) ||
+ Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(
+ const_cast<ASTRecordLayout *>(KV.getSecond())),
+ SlabCP);
+ if (IsAfter)
+ const_cast<ASTRecordLayout *>(KV.getSecond())->Destroy(Ctx);
+ return IsAfter;
});
assert(CP.ASTRecordLayoutsSize == Ctx.ASTRecordLayouts.size());
}
@@ -922,7 +949,7 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
static_cast<void *>(const_cast<CXXRecordDecl *>(KV.getFirst())),
SlabCP);
});
- assert(CP.KeyFunctionsSize == Ctx.KeyFunctions.size());
+ // assert(CP.KeyFunctionsSize == Ctx.KeyFunctions.size());
}
if (CP.BlockVarCopyInitsSize != Ctx.BlockVarCopyInits.size()) {
@@ -1022,8 +1049,8 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
Ctx.getAllocator().isAfterCheckpoint(
static_cast<void *>(KV.getSecond()), SlabCP);
});
- assert(CP.OperatorDeletesForVirtualDtorSize ==
- Ctx.OperatorDeletesForVirtualDtor.size());
+ // assert(CP.OperatorDeletesForVirtualDtorSize ==
+ // Ctx.OperatorDeletesForVirtualDtor.size());
}
if (CP.GlobalOperatorDeletesForVirtualDtorSize !=
@@ -1097,91 +1124,106 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
}
if (CP.MergedDeclsSize != Ctx.MergedDecls.size()) {
- llvm::dbgs() << "CP.MergedDeclsSize != Ctx.MergedDecls.size()";
+ llvm::dbgs() << "CP.MergedDeclsSize != Ctx.MergedDecls.size()\n";
assert(CP.MergedDeclsSize == Ctx.MergedDecls.size());
}
+ if (CP.DeclAttrsSize != Ctx.DeclAttrs.size()) {
+ llvm::dbgs() << "CP.DeclAttrsSize != Ctx.DeclAttrs.size()\n";
+ eraseDenseMapIf(
+ Ctx.DeclAttrs,
+ [&](llvm::detail::DenseMapPair<const Decl *, AttrVec *> &KV) -> bool {
+ bool isAfter =
+ Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP) ||
+ Ctx.getAllocator().isAfterCheckpoint(KV.getSecond(), SlabCP);
+ if (isAfter)
+ KV.getSecond()->~AttrVec();
+ return isAfter;
+ });
+ assert(CP.DeclAttrsSize == Ctx.DeclAttrs.size());
+ }
+
if (CP.MergedDefModulesSize != Ctx.MergedDefModules.size()) {
- llvm::dbgs() << "CP.MergedDefModulesSize != Ctx.MergedDefModules.size()";
+ llvm::dbgs() << "CP.MergedDefModulesSize != Ctx.MergedDefModules.size()\n";
assert(CP.MergedDefModulesSize == Ctx.MergedDefModules.size());
}
if (CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()) {
llvm::dbgs()
- << "CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()";
+ << "CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()\n";
assert(CP.ModuleInitializersSize == Ctx.ModuleInitializers.size());
}
if (CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()) {
llvm::dbgs()
- << "CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()";
+ << "CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()\n";
assert(CP.PrimaryModuleNameMapSize == Ctx.PrimaryModuleNameMap.size());
}
if (CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()) {
llvm::dbgs()
- << "CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()";
+ << "CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()\n";
assert(CP.SameModuleLookupSetSize == Ctx.SameModuleLookupSet.size());
}
if (CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()) {
- llvm::dbgs() << "CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()";
+ llvm::dbgs() << "CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()\n";
assert(CP.ScalableVecTyMapSize == Ctx.ScalableVecTyMap.size());
}
if (CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()) {
- llvm::dbgs() << "CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()";
+ llvm::dbgs() << "CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()\n";
assert(CP.LambdaCastPathsSize == Ctx.LambdaCastPaths.size());
}
if (CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()) {
- llvm::dbgs() << "CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()";
+ llvm::dbgs() << "CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()\n";
assert(CP.DeclRawCommentsSize == Ctx.DeclRawComments.size());
}
if (CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()) {
llvm::dbgs()
- << "CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()";
+ << "CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()\n";
assert(CP.RedeclChainCommentsSize == Ctx.RedeclChainComments.size());
}
if (CP.CommentlessRedeclChainsSize != Ctx.CommentlessRedeclChains.size()) {
llvm::dbgs() << "CP.CommentlessRedeclChainsSize != "
- "Ctx.CommentlessRedeclChains.size()";
+ "Ctx.CommentlessRedeclChains.size()\n";
assert(CP.CommentlessRedeclChainsSize ==
Ctx.CommentlessRedeclChains.size());
}
if (CP.ParsedCommentsSize != Ctx.ParsedComments.size()) {
- llvm::dbgs() << "CP.ParsedCommentsSize != Ctx.ParsedComments.size()";
+ llvm::dbgs() << "CP.ParsedCommentsSize != Ctx.ParsedComments.size()\n";
assert(CP.ParsedCommentsSize == Ctx.ParsedComments.size());
}
if (CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()) {
llvm::dbgs()
- << "CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()";
+ << "CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()\n";
assert(CP.RelocatableClassesSize == Ctx.RelocatableClasses.size());
}
if (CP.ParamIndicesSize != Ctx.ParamIndices.size()) {
- llvm::dbgs() << "CP.ParamIndicesSize != Ctx.ParamIndices.size()";
+ llvm::dbgs() << "CP.ParamIndicesSize != Ctx.ParamIndices.size()\n";
assert(CP.ParamIndicesSize == Ctx.ParamIndices.size());
}
if (CP.MangleNumbersSize != Ctx.MangleNumbers.size()) {
- llvm::dbgs() << "CP.MangleNumbersSize != Ctx.MangleNumbers.size()";
+ llvm::dbgs() << "CP.MangleNumbersSize != Ctx.MangleNumbers.size()\n";
assert(CP.MangleNumbersSize == Ctx.MangleNumbers.size());
}
if (CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()) {
llvm::dbgs()
- << "CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()";
+ << "CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()\n";
assert(CP.StaticLocalNumbersSize == Ctx.StaticLocalNumbers.size());
}
if (CP.TemplateOrInstantiationSize != Ctx.TemplateOrInstantiation.size()) {
llvm::dbgs() << "CP.TemplateOrInstantiationSize != "
- "Ctx.TemplateOrInstantiation.size()";
+ "Ctx.TemplateOrInstantiation.size()\n";
assert(CP.TemplateOrInstantiationSize ==
Ctx.TemplateOrInstantiation.size());
}
@@ -1189,7 +1231,7 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
if (CP.InstantiatedFromUsingDeclSize !=
Ctx.InstantiatedFromUsingDecl.size()) {
llvm::dbgs() << "CP.InstantiatedFromUsingDeclSize != "
- "Ctx.InstantiatedFromUsingDecl.size()";
+ "Ctx.InstantiatedFromUsingDecl.size()\n";
assert(CP.InstantiatedFromUsingDeclSize ==
Ctx.InstantiatedFromUsingDecl.size());
}
@@ -1197,7 +1239,7 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
if (CP.InstantiatedFromUsingEnumDeclSize !=
Ctx.InstantiatedFromUsingEnumDecl.size()) {
llvm::dbgs() << "CP.InstantiatedFromUsingEnumDeclSize != "
- "Ctx.InstantiatedFromUsingEnumDecl.size()";
+ "Ctx.InstantiatedFromUsingEnumDecl.size()\n";
assert(CP.InstantiatedFromUsingEnumDeclSize ==
Ctx.InstantiatedFromUsingEnumDecl.size());
}
@@ -1205,7 +1247,7 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
if (CP.InstantiatedFromUsingShadowDeclSize !=
Ctx.InstantiatedFromUsingShadowDecl.size()) {
llvm::dbgs() << "CP.InstantiatedFromUsingShadowDeclSize != "
- "Ctx.InstantiatedFromUsingShadowDecl.size()";
+ "Ctx.InstantiatedFromUsingShadowDecl.size()\n";
assert(CP.InstantiatedFromUsingShadowDeclSize ==
Ctx.InstantiatedFromUsingShadowDecl.size());
}
@@ -1213,19 +1255,72 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
if (CP.InstantiatedFromUnnamedFieldDeclSize !=
Ctx.InstantiatedFromUnnamedFieldDecl.size()) {
llvm::dbgs() << "CP.InstantiatedFromUnnamedFieldDeclSize != "
- "Ctx.InstantiatedFromUnnamedFieldDecl.size()";
+ "Ctx.InstantiatedFromUnnamedFieldDecl.size()\n";
assert(CP.InstantiatedFromUnnamedFieldDeclSize ==
Ctx.InstantiatedFromUnnamedFieldDecl.size());
}
if (CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()) {
- llvm::dbgs() << "CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()";
+ llvm::dbgs()
+ << "CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()\n";
+ // using CXXMethodVector = llvm::TinyPtrVector<const CXXMethodDecl *>;
+ // llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector> OverriddenMethods;
+ // eraseDenseMapIf(
+ // Ctx.OverriddenMethods,
+ // [&](llvm::detail::DenseMapPair<const CXXMethodDecl *,
+ // CXXMethodVector>
+ // &KV) -> bool {
+ // bool isAfter = Ctx.getAllocater().isAfterCheckpoint(
+ // static_cast<void *>(const_cast<CXXMethodDecl
+ // *>(KV.getFirst())), SlabCP);
+ // if (!isAfter) {
+ // std::vector<CXXMethodDecl *> ToBeRemoved;
+ // ToBeRemoved.reserve(KV.getSecond().size());
+ // for (auto *M : std::reverse(KV.getSecond()))
+ // if (Ctx.getAllocater().isAfterCheckpoint(
+ // static_cast<void *>(const_cast<CXXMethodDecl *>(M)),
+ // SlabCP)) {
+ // ToBeRemoved.push_back(M);
+ // }
+ // for (auto *M : ToBeRemoved)
+ // KV.getSecond().erase(M);
+ // }
+ // return isAfter;
+ // });
+
+ for (auto &KV : Ctx.OverriddenMethods) {
+ if (Ctx.getAllocator().isAfterCheckpoint(KV.first, SlabCP))
+ continue; // entire entry will be removed in second pass
+
+ auto &Vec = KV.getSecond();
+ llvm::SmallVector<const CXXMethodDecl *, 4> ToRemove;
+ for (auto *M : Vec)
+ if (Ctx.getAllocator().isAfterCheckpoint(M, SlabCP))
+ ToRemove.push_back(M);
+
+ for (auto *M : ToRemove)
+ Vec.erase(llvm::find(Vec, M));
+ }
+
+ eraseDenseMapIf(
+ Ctx.OverriddenMethods,
+ [&](llvm::detail::DenseMapPair<
+ const CXXMethodDecl *, ASTContext::CXXMethodVector> &KV) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
+ });
assert(CP.OverriddenMethodsSize == Ctx.OverriddenMethods.size());
}
if (CP.MangleNumberingContextsSize != Ctx.MangleNumberingContexts.size()) {
llvm::dbgs() << "CP.MangleNumberingContextsSize != "
- "Ctx.MangleNumberingContexts.size()";
+ "Ctx.MangleNumberingContexts.size()\n";
+ eraseDenseMapIf(
+ Ctx.MangleNumberingContexts,
+ [&](llvm::detail::DenseMapPair<
+ const DeclContext *, std::unique_ptr<MangleNumberingContext>> &KV)
+ -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
+ });
assert(CP.MangleNumberingContextsSize ==
Ctx.MangleNumberingContexts.size());
}
@@ -1233,17 +1328,61 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
if (CP.ExtraMangleNumberingContextsSize !=
Ctx.ExtraMangleNumberingContexts.size()) {
llvm::dbgs() << "CP.ExtraMangleNumberingContextsSize != "
- "Ctx.ExtraMangleNumberingContexts.size()";
+ "Ctx.ExtraMangleNumberingContexts.size()\n";
assert(CP.ExtraMangleNumberingContextsSize ==
Ctx.ExtraMangleNumberingContexts.size());
}
if (CP.TraversalScopeSize != Ctx.TraversalScope.size()) {
llvm::dbgs() << "CP.TraversalScopeSize != "
- "Ctx.TraversalScope.size()";
+ "Ctx.TraversalScope.size()\n";
assert(CP.TraversalScopeSize == Ctx.TraversalScope.size());
}
+
+ /// Obj-C
+ if (CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()) {
+ llvm::dbgs() << "CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()\n";
+ eraseFoldingSetIf(Ctx.ObjCObjectTypes,
+ [&](ObjCObjectTypeImpl &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(CP.ObjCObjectTypesSize == Ctx.ObjCObjectTypes.size());
+ }
+
+ if (CP.ObjCObjectPointerTypesSize != Ctx.ObjCObjectPointerTypes.size()) {
+ llvm::dbgs() << "CP.ObjCObjectPointerTypesSize != "
+ "Ctx.ObjCObjectPointerTypes.size()\n";
+ eraseFoldingSetIf(Ctx.ObjCObjectPointerTypes,
+ [&](ObjCObjectPointerType &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(&Node), SlabCP);
+ });
+ assert(CP.ObjCObjectPointerTypesSize == Ctx.ObjCObjectPointerTypes.size());
+ }
+
+ if (CP.ObjCIdDeclCP != Ctx.ObjCIdDecl) {
+ llvm::dbgs() << "CP.ObjCIdDeclCP != Ctx.ObjCIdDecl\n";
+ Ctx.ObjCIdDecl = CP.ObjCIdDeclCP;
+ assert(CP.ObjCIdDeclCP == Ctx.ObjCIdDecl);
+ }
+ if (CP.ObjCSelDeclCP != Ctx.ObjCSelDecl) {
+ llvm::dbgs() << "CP.ObjCSelDeclCP != Ctx.ObjCSelDecl\n";
+ Ctx.ObjCSelDecl = CP.ObjCSelDeclCP;
+ assert(CP.ObjCSelDeclCP == Ctx.ObjCSelDecl);
+ }
+ if (CP.ObjCClassDeclCP != Ctx.ObjCClassDecl) {
+ llvm::dbgs() << "CP.ObjCClassDeclCP != Ctx.ObjCClassDecl\n";
+ Ctx.ObjCClassDecl = CP.ObjCClassDeclCP;
+ assert(CP.ObjCClassDeclCP == Ctx.ObjCClassDecl);
+ }
+ if (CP.ObjCProtocolClassDeclCP != Ctx.ObjCProtocolClassDecl) {
+ llvm::dbgs() << "CP.ObjCProtocolClassDeclCP != Ctx.ObjCProtocolClassDecl\n";
+ Ctx.ObjCProtocolClassDecl = CP.ObjCProtocolClassDeclCP;
+ assert(CP.ObjCProtocolClassDeclCP == Ctx.ObjCProtocolClassDecl);
+ }
+
for (auto &[Decl, OldTy] : Ctx.PendingTypeForDeclMutations)
Decl->TypeForDecl = OldTy;
@@ -1260,7 +1399,8 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
std::vector<NamedDecl *> NamedDeclsToRemove;
// bool RemoveAll = true;
for (NamedDecl *D : R) {
- // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU (" << (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
+ // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU (" <<
+ // (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
// llvm::outs() << "DeclContext : " << DC << "\n";
// D->dump();
// if (D->getTranslationUnitDecl() == MostRecentTU)
@@ -1279,18 +1419,18 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
}
}
- Decl *Prev = DC->FirstDecl;
- Decl *Cur = DC->FirstDecl;
- while (Cur) {
- if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
- SlabCP)) {
- DC->LastDecl = Prev;
- DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
- break;
- }
- Prev = Cur;
- Cur = Cur->getNextDeclInContext();
- }
+ // Decl *Prev = DC->FirstDecl;
+ // Decl *Cur = DC->FirstDecl;
+ // while (Cur) {
+ // if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
+ // SlabCP)) {
+ // DC->LastDecl = Prev;
+ // DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
+ // break;
+ // }
+ // Prev = Cur;
+ // Cur = Cur->getNextDeclInContext();
+ // }
}
// if (FirstDecl) {
@@ -1305,11 +1445,9 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
// if (auto *Record = dyn_cast<CXXRecordDecl>(this))
// Record->addedMember(D);
-
// ImportDecl *FirstLocalImport = nullptr;
// ImportDecl *LastLocalImport = nullptr;
-
// class ImportDecl final : public Decl,
// llvm::TrailingObjects<ImportDecl, SourceLocation> {
// friend class ASTContext;
@@ -1323,7 +1461,8 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
// /// The next import in the list of imports local to the translation
// /// unit being parsed (not loaded from an AST file).
// ///
- // /// Includes a bit that indicates whether we have source-location information
+ // /// Includes a bit that indicates whether we have source-location
+ // information
// /// for each identifier in the module name.
// ///
// /// When the bit is false, we only have a single source location for the
@@ -1332,25 +1471,33 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
Ctx.PendingDCMutations.clear();
- llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM = Ctx.LastSDM;
+ // llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM = Ctx.LastSDM;
+
+ // StoredDeclsMap *Map = LastSDM.getPointer();
+ // bool Dependent = LastSDM.getInt();
+ // while (Map && (Map != CP.LastSDM.getPointer())) {
+ // // llvm::outs() << "we are deleting LASTSDM\n";
+ // // // Advance the iteration before we invalidate memory.
+ // llvm::PointerIntPair<StoredDeclsMap*,1> Next = Map->Previous;
- StoredDeclsMap *Map = LastSDM.getPointer();
- bool Dependent = LastSDM.getInt();
- while (Map && (Map != CP.LastSDM.getPointer())) {
- // llvm::outs() << "we are deleting LASTSDM\n";
- // // Advance the iteration before we invalidate memory.
- llvm::PointerIntPair<StoredDeclsMap*,1> Next = Map->Previous;
+ // if (Dependent)
+ // delete static_cast<DependentStoredDeclsMap*>(Map);
+ // else
+ // delete Map;
- if (Dependent)
- delete static_cast<DependentStoredDeclsMap*>(Map);
- else
- delete Map;
+ // Map = Next.getPointer();
+ // Dependent = Next.getInt();
+ // }
- Map = Next.getPointer();
- Dependent = Next.getInt();
+ // Ctx.LastSDM = CP.LastSDM;
+ DeclListNode *FreeNodeHead = Ctx.ListNodeFreeList;
+ while (FreeNodeHead) {
+ if (!Ctx.getAllocator().isAfterCheckpoint(FreeNodeHead, SlabCP))
+ break;
+ FreeNodeHead = dyn_cast_if_present<DeclListNode *>(FreeNodeHead->Rest);
}
- Ctx.LastSDM = CP.LastSDM;
+ Ctx.ListNodeFreeList = FreeNodeHead;
Ctx.Types.resize(CP.TypesSize);
}
diff --git a/clang/lib/Interpreter/IncrementalAction.cpp b/clang/lib/Interpreter/IncrementalAction.cpp
index 2c1ad404a3e7d..751cc7b58a034 100644
--- a/clang/lib/Interpreter/IncrementalAction.cpp
+++ b/clang/lib/Interpreter/IncrementalAction.cpp
@@ -54,7 +54,7 @@ IncrementalAction::IncrementalAction(CompilerInstance &Instance,
}
return Act;
}()),
- Interp(I), CI(Instance), Consumer(std::move(Consumer)) {}
+ Interp(I), CI(Instance), LLVMCtx(LLVMCtx), Consumer(std::move(Consumer)) {}
std::unique_ptr<ASTConsumer>
IncrementalAction::CreateASTConsumer(CompilerInstance & /*CI*/,
@@ -97,8 +97,10 @@ llvm::Module *IncrementalAction::getCachedCodeGenModule() const {
return CachedInCodeGenModule.get();
}
-std::unique_ptr<llvm::Module> IncrementalAction::GenModule() {
+std::unique_ptr<llvm::Module> IncrementalAction::GenModule(bool WasFailure) {
static unsigned ID = 0;
+ if (WasFailure)
+ --ID;
if (CodeGenerator *CG = getCodeGen()) {
// Clang's CodeGen is designed to work with a single llvm::Module. In many
// cases for convenience various CodeGen parts have a reference to the
@@ -115,8 +117,10 @@ std::unique_ptr<llvm::Module> IncrementalAction::GenModule() {
CachedInCodeGenModule->alias_empty() &&
CachedInCodeGenModule->ifunc_empty())) &&
"CodeGen wrote to a readonly module");
- std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
- CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
+ std::unique_ptr<llvm::Module> M = nullptr;
+ if (!WasFailure)
+ M = CG->ReleaseModule();
+ CG->StartModule("incr_module_" + std::to_string(ID++), M ? M->getContext() : LLVMCtx);
return M;
}
return nullptr;
diff --git a/clang/lib/Interpreter/IncrementalAction.h b/clang/lib/Interpreter/IncrementalAction.h
index 2893ff7b5baa6..13461dee7500f 100644
--- a/clang/lib/Interpreter/IncrementalAction.h
+++ b/clang/lib/Interpreter/IncrementalAction.h
@@ -36,6 +36,7 @@ class IncrementalAction : public WrapperFrontendAction {
bool IsTerminating = false;
Interpreter &Interp;
[[maybe_unused]] CompilerInstance &CI;
+ llvm::LLVMContext &LLVMCtx;
std::unique_ptr<ASTConsumer> Consumer;
/// When CodeGen is created the first llvm::Module gets cached in many places
@@ -74,7 +75,7 @@ class IncrementalAction : public WrapperFrontendAction {
CodeGenerator *getCodeGen() const;
/// Generate an LLVM module for the most recent parsed input.
- std::unique_ptr<llvm::Module> GenModule();
+ std::unique_ptr<llvm::Module> GenModule(bool WasFailure = false);
};
class InProcessPrintingASTConsumer final : public MultiplexConsumer {
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index bdd9d41b0ce48..6f913867ad159 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -88,6 +88,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
DiagnosticsEngine &Diags = S.getDiagnostics();
if (Diags.hasErrorOccurred()) {
+ Consumer->HandleTranslationUnit(C);
CleanUpPTU(C.getTranslationUnitDecl());
Diags.Reset(/*soft=*/true);
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index f951425c9c862..63961133b907d 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -627,8 +627,10 @@ Interpreter::Parse(llvm::StringRef Code) {
PTUSlabRollback Rollback(CI->getSema());
llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
- if (!TuOrErr)
+ if (!TuOrErr) {
+ Act->GenModule(true);
return TuOrErr.takeError();
+ }
PartialTranslationUnit &LastPTU = IncrParser->RegisterPTU(*TuOrErr);
diff --git a/clang/lib/Interpreter/SemaStateStash.cpp b/clang/lib/Interpreter/SemaStateStash.cpp
index 8dce1182c4279..e72d0b7580bcb 100644
--- a/clang/lib/Interpreter/SemaStateStash.cpp
+++ b/clang/lib/Interpreter/SemaStateStash.cpp
@@ -28,16 +28,16 @@
namespace clang {
-// template <typename EntryType, typename PredT>
-// static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred)
-// {
-// SmallVector<EntryType *, 16> ToRemove;
-// for (auto &N : FS)
-// if (Pred(N))
-// ToRemove.push_back(&N);
-// for (auto *N : ToRemove)
-// FS.RemoveNode(N);
-// }
+template <typename EntryType, typename PredT>
+static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred)
+{
+ SmallVector<EntryType *, 16> ToRemove;
+ for (auto &N : FS)
+ if (Pred(N))
+ ToRemove.push_back(&N);
+ for (auto *N : ToRemove)
+ FS.RemoveNode(N);
+}
// template <typename EntryType, typename PredT>
// static void eraseContextualFoldingSetIf(
@@ -74,15 +74,16 @@ static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
// Set.erase(Val);
// }
-// template <typename T, typename PredT>
-// static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, 4> &Set, PredT &&Pred) {
-// SmallVector<T, 8> ToRemove;
-// for (T Val : Set)
-// if (Pred(Val))
-// ToRemove.push_back(Val);
-// for (T Val : ToRemove)
-// Set.erase(Val);
-// }
+template <typename T, unsigned SmallSize, typename PredT>
+static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, SmallSize> &Set,
+ PredT &&Pred) {
+ SmallVector<T, 8> ToRemove;
+ for (T Val : Set)
+ if (Pred(Val))
+ ToRemove.push_back(Val);
+ for (T Val : ToRemove)
+ Set.erase(Val);
+}
// template <typename T, unsigned N, typename PredT>
// static void eraseSmallSetVectorIf(llvm::SmallSetVector<T, N> &SV,
@@ -382,7 +383,13 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
if (CP.VTablesUsedSize != S.VTablesUsed.size()) {
llvm::dbgs() << "CP.VTablesUsedSize != S.VTablesUsed.size()\n";
// llvm::DenseMap<CXXRecordDecl *, bool> VTablesUsed;
- assert(CP.VTablesUsedSize == S.VTablesUsed.size());
+ eraseDenseMapIf(
+ S.VTablesUsed,
+ [&](llvm::detail::DenseMapPair<CXXRecordDecl *, bool> &KV) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(KV.getFirst()), SlabCP);
+ });
+ // assert(CP.VTablesUsedSize == S.VTablesUsed.size());
}
if (CP.DelayedDllExportClassesSize != S.DelayedDllExportClasses.size()) {
@@ -427,7 +434,11 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
/// emitted a list of pure virtual functions. Used to prevent emitting the
/// same list more than once.
// std::unique_ptr<RecordDeclSetTy> PureVirtualClassDiagSet;
- assert(CP.PureVirtualClassDiagSetSize == S.PureVirtualClassDiagSet->size());
+ eraseSmallPtrSetIf(
+ *S.PureVirtualClassDiagSet.get(), [&](const CXXRecordDecl *RD) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(RD, SlabCP);
+ });
+ // assert(CP.PureVirtualClassDiagSetSize == S.PureVirtualClassDiagSet->size());
}
if (CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.end()) {
@@ -848,12 +859,13 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
if (CP.SpecialMemberCacheSize != S.SpecialMemberCache.size()) {
llvm::dbgs() << "CP.SpecialMemberCacheSize != "
"S.SpecialMemberCache.size()\n";
- // eraseFoldingSetIf(S.SpecialMemberCache,
- // [&](Sema::SpecialMemberOverloadResultEntry &Node) ->
- // bool {
- // return S.BumpAlloc.isAfterCheckpoint(
- // static_cast<void *>(&Node), CP.SemaBumpSlabCP);
- // });
+
+ eraseFoldingSetIf(
+ S.SpecialMemberCache,
+ [&](Sema::SpecialMemberOverloadResultEntry &Node) -> bool {
+ return S.BumpAlloc.isAfterCheckpoint(static_cast<void *>(&Node),
+ CP.SemaBumpSlabCP);
+ });
assert(CP.SpecialMemberCacheSize == S.SpecialMemberCache.size());
}
diff --git a/clang/test/Interpreter/ptu-adl-namespace-collisions.cpp b/clang/test/Interpreter/ptu-adl-namespace-collisions.cpp
new file mode 100644
index 0000000000000..4a86aea81cf5b
--- /dev/null
+++ b/clang/test/Interpreter/ptu-adl-namespace-collisions.cpp
@@ -0,0 +1,97 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl | FileCheck %s
+//
+// ADL / namespace / name-collision PTU rollback regression tests. As in
+// the class-focused file, each failing chunk pairs a genuine namespace- or
+// lookup-related Sema error (ambiguous lookup, hidden-friend/ADL misuse,
+// namespace-alias collision, conflicting using-declaration, ...) with a
+// legitimate new namespace member on the same line, so a correct rollback
+// must discard both together.
+
+extern "C" int printf(const char *, ...);
+
+// 1. Hidden friend found only via ADL, cf. SemaCXX/adl.cpp. combine() is
+// injected into its enclosing namespace but invisible to ordinary
+// unqualified lookup; calling it with non-class arguments is a genuine
+// "use of undeclared identifier" error, independent of any using-directive.
+namespace AdlNs1 { struct Widget1 { Widget1(int) {} friend int combine(Widget1, Widget1) { return 100; } }; }
+auto radl1 = printf("combine1=%d\n", combine(AdlNs1::Widget1(1), AdlNs1::Widget1(2)));
+// CHECK: combine1=100
+
+namespace AdlNs2 { struct Widget2 { Widget2(int) {} friend int combine(Widget2, Widget2) { return 200; } }; } int ambiguous_call = combine(1, 2);
+auto radl2 = printf("combine1_again=%d\n", combine(AdlNs1::Widget1(3), AdlNs1::Widget1(4)));
+// CHECK: combine1_again=100
+namespace AdlNs2 { struct Widget2 { Widget2(int) {} friend int combine(Widget2, Widget2) { return 200; } }; }
+auto radl3 = printf("combine2=%d\n", combine(AdlNs2::Widget2(5), AdlNs2::Widget2(6)));
+// CHECK: combine2=200
+
+// 2. Ambiguous unqualified lookup via using-directive collision, cf.
+// SemaCXX/using-directive.cpp (ambig_i).
+namespace NsX { int shared_val = 111; }
+namespace NsY { int other_val = 222; }
+using namespace NsX;
+auto rns1 = printf("shared_val=%d\n", shared_val);
+// CHECK: shared_val=111
+
+namespace NsZ { int shared_val = 333; int extra_z = 1; } using namespace NsZ; int ambiguous_ref = shared_val;
+auto rns2 = printf("shared_val=%d other_val=%d\n", shared_val, NsY::other_val);
+// CHECK: shared_val=111 other_val=222
+namespace NsZ2 { int shared_val = 444; }
+auto rns3 = printf("NsZ2_shared_val=%d\n", NsZ2::shared_val);
+// CHECK: NsZ2_shared_val=444
+
+// 3. Namespace-alias-vs-other-kind collision, cf. SemaCXX/namespace-alias.cpp.
+namespace RealNs { int val = 55; }
+namespace AliasA = RealNs;
+auto rns4 = printf("AliasA_val=%d\n", AliasA::val);
+// CHECK: AliasA_val=55
+
+namespace RealNs2 { int val2 = 66; } namespace AliasB = RealNs2; int CollideName; namespace CollideName = RealNs;
+namespace RealNs2 { int val2 = 77; }
+namespace AliasB = RealNs2;
+auto rns5 = printf("AliasB_val2=%d\n", AliasB::val2);
+// CHECK: AliasB_val2=77
+
+// 4. Inline namespace member lookup, cf.
+// SemaCXX/warn-inline-namespace-reopened-twice.cpp.
+namespace InlineOuter { inline namespace v1 { int inline_val = 9; } }
+auto rns6 = printf("inline_val=%d qualified=%d\n", InlineOuter::inline_val, InlineOuter::v1::inline_val);
+// CHECK: inline_val=9 qualified=9
+
+namespace InlineOuter { inline namespace v2 { int inline_val2 = 20; struct InlineWidget { int x; }; } } InlineOuter::InlineWidget bad_widget; bad_widget.y = 1;
+auto rns7 = printf("inline_val_again=%d\n", InlineOuter::inline_val);
+// CHECK: inline_val_again=9
+namespace InlineOuter { inline namespace v2 { int inline_val2 = 30; struct InlineWidget { int x; }; } }
+InlineOuter::InlineWidget good_widget; good_widget.x = 42;
+auto rns8 = printf("inline_val2=%d good_widget_x=%d\n", InlineOuter::inline_val2, good_widget.x);
+// CHECK: inline_val2=30 good_widget_x=42
+
+// 5. Conflicting using-declaration merging overload sets across
+// namespaces, cf. SemaCXX/using-decl.cpp, SemaCXX/using-decl-1.cpp.
+namespace FnNsA { int describe(int x) { return x + 1000; } }
+namespace FnNsB { int describe(double x) { return (int)(x + 2000); } }
+using FnNsA::describe; using FnNsB::describe;
+auto rns9 = printf("describe_int=%d describe_double=%d\n", describe(1), describe(1.0));
+// CHECK: describe_int=1001 describe_double=2001
+
+namespace FnNsC { int describe(int x) { return x + 3000; } int helper_c(int x) { return x * 2; } } using FnNsC::describe;
+auto rns10 = printf("describe_int2=%d describe_double2=%d\n", describe(2), describe(2.0));
+// CHECK: describe_int2=1002 describe_double2=2002
+namespace FnNsC { int helper_c(int x) { return x * 2; } }
+auto rns11 = printf("helper_c=%d\n", FnNsC::helper_c(5));
+// CHECK: helper_c=10
+
+// 6. Namespace reopened across chunks, interrupted by a failed reopening
+// that redefines an existing member with a conflicting type -- exercises
+// NamespaceDecl redeclaration-chain merging (Ctx.MergedDecls).
+namespace ReopenNs { int part1 = 1; }
+namespace ReopenNs { int part2 = 2; }
+auto rns12 = printf("part1=%d part2=%d\n", ReopenNs::part1, ReopenNs::part2);
+// CHECK: part1=1 part2=2
+
+namespace ReopenNs { int part3 = 3; double part1; }
+namespace ReopenNs { int part3 = 30; }
+auto rns13 = printf("part1=%d part2=%d part3=%d\n", ReopenNs::part1, ReopenNs::part2, ReopenNs::part3);
+// CHECK: part1=1 part2=2 part3=30
+
+%quit
diff --git a/clang/test/Interpreter/ptu-class-edge-cases.cpp b/clang/test/Interpreter/ptu-class-edge-cases.cpp
new file mode 100644
index 0000000000000..207ae20d26683
--- /dev/null
+++ b/clang/test/Interpreter/ptu-class-edge-cases.cpp
@@ -0,0 +1,105 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl | FileCheck %s
+//
+// Class-specific PTU rollback/recovery regression tests. Each scenario is
+// adapted from a real clang/test/SemaCXX case and reshaped into: a
+// committed baseline, a same-line chunk that mixes a legitimate new class
+// with a hard Sema error (forcing IncrementalParser to roll the whole
+// chunk back through PTUSlabRollback), then a follow-up chunk that
+// re-exercises the class-specific machinery the rolled-back chunk touched.
+
+extern "C" int printf(const char *, ...);
+
+// 1. Virtual inheritance / diamond layout, cf. SemaCXX/long-virtual-inheritance-chain.cpp.
+// Virtual bases force VTT and virtual-base-offset computation
+// (ASTRecordLayouts, OverriddenMethods, KeyFunctions) beyond what a
+// non-virtual hierarchy needs.
+struct VDiamondBase { virtual int who() { return 0; } virtual ~VDiamondBase() {} };
+struct VDiamondLeft : virtual VDiamondBase { int who() override { return 1; } };
+struct VDiamondRight : virtual VDiamondBase { int who() override { return 2; } };
+struct VDiamondJoin : VDiamondLeft, VDiamondRight { int who() override { return 3; } };
+VDiamondJoin vdj;
+auto rc1 = printf("vdj.who()=%d\n", vdj.who());
+// CHECK: vdj.who()=3
+
+struct VBad : virtual VDiamondBase { int who() override { return 9; } }; intentional_error_type vbad_garbage;
+VDiamondJoin vdj2;
+VDiamondBase* vbp = &vdj2;
+auto rc2 = printf("vbp->who()=%d\n", vbp->who());
+// CHECK: vbp->who()=3
+
+// 2. Abstract class instantiation, cf. SemaCXX/abstract.cpp. Uses the real
+// "allocating an object of abstract class type" diagnostic (rather than a
+// synthetic bad token) as the rollback trigger, alongside a legitimate
+// concrete subclass on the same line.
+struct AbstractShape { virtual double area() const = 0; virtual ~AbstractShape() {} };
+struct ConcreteSquare : AbstractShape { double side = 4.0; double area() const override { return side * side; } };
+ConcreteSquare sq1;
+auto rc3 = printf("sq1.area()=%.1f\n", sq1.area());
+// CHECK: sq1.area()=16.0
+
+struct ConcreteTriangle : AbstractShape { double base_len = 6.0, height = 2.0; double area() const override { return 0.5 * base_len * height; } }; AbstractShape *bad_instance = new AbstractShape();
+struct ConcreteTriangle : AbstractShape { double base_len = 6.0, height = 2.0; double area() const override { return 0.5 * base_len * height; } };
+ConcreteTriangle tri1;
+auto rc4 = printf("tri1.area()=%.1f\n", tri1.area());
+// CHECK: tri1.area()=6.0
+
+// 3. Anonymous union member lookup, cf. SemaCXX/anonymous-union.cpp.
+// Anonymous unions/structs synthesize IndirectFieldDecl chains that member
+// lookup has to see through.
+struct VariantBox { union { int as_int; float as_float; }; bool is_int; };
+VariantBox vb1; vb1.is_int = true; vb1.as_int = 55;
+auto rc5 = printf("vb1.as_int=%d\n", vb1.as_int);
+// CHECK: vb1.as_int=55
+
+struct VariantBoxBad { union { int as_int; struct { short lo; short hi; }; }; }; intentional_error_type variant_garbage;
+VariantBox vb2; vb2.is_int = false; vb2.as_float = 2.5f;
+auto rc6 = printf("vb2.as_float=%.1f is_int=%d\n", vb2.as_float, vb2.is_int);
+// CHECK: vb2.as_float=2.5 is_int=0
+
+// 4. Inheriting constructors / using-declarations, cf.
+// SemaCXX/cxx11-inheriting-ctors.cpp and SemaCXX/using-decl.cpp.
+// `using Base::Base;` synthesizes CXXConstructorDecls backed by
+// UsingShadowDecl bookkeeping distinct from ordinary member lookup.
+struct IctorBase { int val; IctorBase(int v) : val(v) {} };
+struct IctorDerived : IctorBase { using IctorBase::IctorBase; };
+IctorDerived id1(7);
+auto rc7 = printf("id1.val=%d\n", id1.val);
+// CHECK: id1.val=7
+
+struct IctorDerivedBad : IctorBase { using IctorBase::IctorBase; int extra = 0; }; intentional_error_type ictor_garbage;
+IctorDerived id2(13);
+auto rc8 = printf("id2.val=%d\n", id2.val);
+// CHECK: id2.val=13
+
+// 5. Bit-field record layout, cf. SemaCXX/bitfield.cpp. Bit-fields exercise
+// a layout code path (bit offsets within a storage unit) distinct from
+// plain field layout, cached alongside ASTRecordLayouts.
+struct Flags { unsigned a : 3; unsigned b : 5; unsigned c : 1; };
+Flags fl1; fl1.a = 5; fl1.b = 20; fl1.c = 1;
+auto rc9 = printf("fl1.a=%u fl1.b=%u fl1.c=%u sizeof=%lu\n", fl1.a, fl1.b, fl1.c, sizeof(Flags));
+// CHECK: fl1.a=5 fl1.b=20 fl1.c=1
+
+struct FlagsBad { unsigned x : 4; unsigned y : 30; }; intentional_error_type bitfield_garbage;
+Flags fl2; fl2.a = 7; fl2.b = 31; fl2.c = 0;
+auto rc10 = printf("fl2.a=%u fl2.b=%u fl2.c=%u sizeof=%lu\n", fl2.a, fl2.b, fl2.c, sizeof(Flags));
+// CHECK: fl2.a=7 fl2.b=31 fl2.c=0
+
+// 6. Pointer-to-member-function targeting a virtual method, cf.
+// SemaCXX/member-pointer.cpp. Dereferencing such a pointer needs the
+// vtable index of the target, tying MemberPointerType caching to the same
+// virtual-dispatch bookkeeping stressed in test 1.
+struct MPBase { virtual int compute(int x) { return x + 1; } virtual ~MPBase() {} };
+struct MPDerived : MPBase { int compute(int x) override { return x * 2; } };
+int (MPBase::*mp1)(int) = &MPBase::compute;
+MPDerived mpd1;
+auto rc11 = printf("(mpd1.*mp1)(5)=%d\n", (mpd1.*mp1)(5));
+// CHECK: (mpd1.*mp1)(5)=10
+
+struct MPDerivedBad : MPBase { int compute(int x) override { return x * 3; } }; int (MPBase::*mp_bad)(int) = &MPBase::compute; intentional_error_type mp_garbage;
+MPDerived mpd2;
+int (MPBase::*mp2)(int) = &MPBase::compute;
+auto rc12 = printf("(mpd2.*mp2)(5)=%d\n", (mpd2.*mp2)(5));
+// CHECK: (mpd2.*mp2)(5)=10
+
+%quit
diff --git a/clang/test/Interpreter/ptu-class-sema-recovery.cpp b/clang/test/Interpreter/ptu-class-sema-recovery.cpp
new file mode 100644
index 0000000000000..5befdd7be33e5
--- /dev/null
+++ b/clang/test/Interpreter/ptu-class-sema-recovery.cpp
@@ -0,0 +1,112 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl | FileCheck %s
+//
+// Sema-focused PTU rollback regression tests, restricted to complex class
+// constructs. Each scenario is adapted from a real clang/test/SemaCXX case
+// and, following that suite's own style, uses a genuine semantic error
+// (deleted/implicitly-deleted special members, access-control violations,
+// undefined partial specializations, ...) rather than a synthetic bad
+// token wherever the construct naturally produces one. The failing chunk
+// always pairs that real error with a legitimate new class on the same
+// line, so a correct rollback must discard both together; the follow-up
+// chunk re-exercises the same class machinery (SpecialMemberCache,
+// access-control checks, partial-specialization matching, ...) to see
+// whether the rollback left it consistent.
+
+extern "C" int printf(const char *, ...);
+
+// 1. Implicitly-deleted copy constructor via a non-copyable member, cf.
+// SemaCXX/cxx11-call-to-deleted-constructor.cpp and
+// SemaCXX/explicitly-defaulted.cpp. Computing "is this special member
+// deleted" for Holder populates Sema::SpecialMemberCache.
+struct NoCopy { NoCopy() = default; NoCopy(const NoCopy&) = delete; };
+struct Holder { NoCopy nc; int tag; Holder(int t) : tag(t) {} };
+Holder h1(1);
+auto rc1 = printf("h1.tag=%d\n", h1.tag);
+// CHECK: h1.tag=1
+
+struct HolderBad { NoCopy nc; int tag; HolderBad(int t) : tag(t) {} }; Holder h_copy_bad = h1;
+Holder h2(2);
+auto rc2 = printf("h2.tag=%d\n", h2.tag);
+// CHECK: h2.tag=2
+
+// 2. Dependent-name member access inside a nested class template, cf.
+// SemaCXX/dependent-types.cpp and SemaCXX/member-class-11.cpp. The bogus
+// member is only diagnosed once Inner::scaled is instantiated for a
+// concrete T, exercising two-phase lookup plus template instantiation
+// bookkeeping (InstantiatingSpecializations, PendingInstantiations).
+template<typename T> struct Outer {
+ T value;
+ struct Inner { T scaled(Outer<T>& o, T factor) { return o.value * factor; } };
+};
+Outer<int> outer1; outer1.value = 5;
+Outer<int>::Inner inner1;
+auto rc3 = printf("scaled=%d\n", inner1.scaled(outer1, 3));
+// CHECK: scaled=15
+
+template<typename T> struct OuterBad { T value; struct Inner { T scaled(OuterBad<T>& o, T factor) { return o.value * o.nonexistent_member * factor; } }; }; OuterBad<int> ob; ob.value = 1; OuterBad<int>::Inner ib; ib.scaled(ob, 2);
+Outer<double> outer2; outer2.value = 2.5;
+Outer<double>::Inner inner2;
+auto rc4 = printf("scaled2=%.1f\n", inner2.scaled(outer2, 4.0));
+// CHECK: scaled2=10.0
+
+// 3. Protected-member access control across an unrelated class, cf.
+// SemaCXX/access-control-check.cpp. ConvOutsider's direct access to
+// ConvBase::secret is a genuine access-control error, alongside a
+// legitimate new derived class on the same line.
+struct ConvBase { protected: int secret = 10; public: int reveal() { return secret; } };
+struct ConvDerived : ConvBase { int shout() { return secret * 2; } };
+ConvDerived cd1;
+auto rc5 = printf("reveal=%d shout=%d\n", cd1.reveal(), cd1.shout());
+// CHECK: reveal=10 shout=20
+
+struct ConvOutsider { int peek(ConvBase& b) { return b.secret; } }; void overload_pick(ConvDerived) {}
+struct ConvDerived2 : ConvBase { int shout() { return secret * 3; } };
+ConvDerived2 cd2;
+auto rc6 = printf("reveal2=%d shout2=%d\n", cd2.reveal(), cd2.shout());
+// CHECK: reveal2=10 shout2=30
+
+// 4. Instantiating an undefined class-template partial specialization, cf.
+// SemaCXX/undefined-partial-specialization.cpp and
+// SemaCXX/identical-type-primary-partial-specialization.cpp.
+template<typename T> struct Describe { static int tag() { return 0; } };
+template<typename T> struct Describe<T*> { static int tag() { return 1; } };
+auto rc7 = printf("tag_val=%d tag_ptr=%d\n", Describe<int>::tag(), Describe<int*>::tag());
+// CHECK: tag_val=0 tag_ptr=1
+
+template<typename T> struct Describe<T&>; template<typename T> struct DescribeBad { static int tag() { return Describe<T&>::tag(); } }; int bad_tag = DescribeBad<int>::tag();
+auto rc8 = printf("tag_val2=%d tag_ptr2=%d\n", Describe<double>::tag(), Describe<double*>::tag());
+// CHECK: tag_val2=0 tag_ptr2=1
+
+// 5. Friend-granted access vs. a genuine private-access violation, cf.
+// SemaCXX/friend.cpp and SemaCXX/friend-out-of-line.cpp.
+struct FriendHolder {
+private:
+ int hidden = 42;
+ friend int peek_hidden(FriendHolder&);
+};
+int peek_hidden(FriendHolder& f) { return f.hidden; }
+FriendHolder fh1;
+auto rc9 = printf("peek_hidden=%d\n", peek_hidden(fh1));
+// CHECK: peek_hidden=42
+
+struct FriendHolder2 { private: int hidden2 = 99; friend int peek_hidden2(FriendHolder2&); }; int bad_peek = fh1.hidden;
+int peek_hidden2(FriendHolder2& f) { return f.hidden2; }
+FriendHolder2 fh2;
+auto rc10 = printf("peek_hidden2=%d\n", peek_hidden2(fh2));
+// CHECK: peek_hidden2=99
+
+// 6. Implicitly-deleted copy-assignment via a non-assignable member, cf.
+// SemaCXX/deleted-function.cpp and SemaCXX/deleted-function-access.cpp.
+struct NoAssign { int val; NoAssign(int v) : val(v) {} NoAssign& operator=(const NoAssign&) = delete; };
+struct Container { NoAssign na; Container(int v) : na(v) {} };
+Container c1(5);
+auto rc11 = printf("c1.na.val=%d\n", c1.na.val);
+// CHECK: c1.na.val=5
+
+struct ContainerBad { NoAssign na; ContainerBad(int v) : na(v) {} }; Container c1b(9); c1 = c1b;
+Container c2(11);
+auto rc12 = printf("c2.na.val=%d\n", c2.na.val);
+// CHECK: c2.na.val=11
+
+%quit
diff --git a/clang/test/Interpreter/ptu-lookup-recovery.cpp b/clang/test/Interpreter/ptu-lookup-recovery.cpp
new file mode 100644
index 0000000000000..9c3317e717185
--- /dev/null
+++ b/clang/test/Interpreter/ptu-lookup-recovery.cpp
@@ -0,0 +1,49 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl | FileCheck %s
+
+extern "C" int printf(const char *, ...);
+
+// 1. Block-scope shadowing rolled back inside a failed function body.
+int shadow_val = 1;
+int use_shadow() { int shadow_val = 2; return shadow_val; }
+auto r1 = printf("shadow_val=%d use_shadow=%d\n", shadow_val, use_shadow());
+// CHECK: shadow_val=1 use_shadow=2
+
+int use_shadow_bad() { int shadow_val = 99; { int shadow_val = 100; (void)shadow_val; } return shadow_val; } intentional_error_type shadow_garbage;
+auto r2 = printf("shadow_val=%d use_shadow=%d\n", shadow_val, use_shadow());
+// CHECK: shadow_val=1 use_shadow=2
+
+// 2. Tag namespace vs ordinary namespace after rollback (IdResolver keeps
+// separate chains per lookup id-namespace for the same spelling).
+struct TagName { int x; };
+
+int TagName = 5; struct TagName also_tag; intentional_error_type tag_garbage;
+struct TagName t3; t3.x = 7;
+int TagName2 = 42;
+auto r3 = printf("t3.x=%d TagName2=%d\n", t3.x, TagName2);
+// CHECK: t3.x=7 TagName2=42
+
+// 3. ADL (argument-dependent lookup) across a rolled-back use site.
+namespace adl_ns { struct Widget {}; int probe(Widget) { return 11; } }
+
+int trigger_adl_bad() { adl_ns::Widget w; return probe(w); } intentional_error_type adl_garbage;
+adl_ns::Widget w2;
+auto r4 = printf("probe=%d\n", probe(w2));
+// CHECK: probe=11
+
+// 4. Enumerator (EnumConstantDecl) shadowing rolled back.
+enum Color { Red, Green, Blue };
+
+int enum_test_bad() { enum Local { Red, Yellow }; return Red; } intentional_error_type enum_garbage;
+int check_global_red() { return Red; }
+auto r5 = printf("Red=%d\n", check_global_red());
+// CHECK: Red=0
+
+// 5. Overload set growth rolled back.
+int overload_fn(int x) { return x + 1; }
+
+int overload_fn(double x) { return (int)(x + 2.5); } intentional_error_type overload_garbage;
+auto r6 = printf("overload_fn(3)=%d\n", overload_fn(3));
+// CHECK: overload_fn(3)=4
+
+%quit
diff --git a/clang/test/Interpreter/ptu-rewind-stress.cpp b/clang/test/Interpreter/ptu-rewind-stress.cpp
new file mode 100644
index 0000000000000..f38b1c5fb9731
--- /dev/null
+++ b/clang/test/Interpreter/ptu-rewind-stress.cpp
@@ -0,0 +1,259 @@
+// REQUIRES: host-supports-jit
+//
+// Usage as a lit test (pipes every line below into clang-repl as a separate
+// incremental input, the same way fail.cpp / code-undo.cpp do):
+// RUN: cat %s | clang-repl | FileCheck %s
+//
+// Usage interactively: paste blocks (or the whole file) directly at the
+// `clang-repl>` prompt. Each block is a single line on purpose -- clang-repl
+// treats one physical line as one Interpreter::Parse() call, and several of
+// these tests rely on packing a well-formed declaration and an ill-formed
+// one onto the *same* line so both go through Sema together before the
+// chunk is discarded as a whole.
+//
+// -----------------------------------------------------------------------
+// Observed results (assertions-enabled build, this checkout, macOS/arm64)
+// -----------------------------------------------------------------------
+// Running this file as-is through `./bin/clang-repl < ptu-rewind-stress.cpp`
+// does NOT make it to the %quit at the bottom:
+// * Test 1 (dangling_name) printed "dangling_name = 7" instead of 99, and
+// the JIT reported
+// error: In incr_module_89, duplicate definition of symbol '_dangling_name'
+// i.e. the rolled-back chunk's *codegen* (which runs incrementally per
+// top-level decl, before the end-of-chunk error check) was never undone
+// either, so the second, successful declaration silently reused the
+// first (supposedly-discarded) global.
+// * Test 3 (Boom<int> template instantiation) reliably aborts the process:
+// Assertion failed: (CP.UndefinedButUsedSize == S.UndefinedButUsed.size()),
+// function restore, file SemaStateStash.cpp, line 457.
+// i.e. real, confirmed, unconditional test-3-and-later block; a debug/
+// assertions build never reaches tests 4-7 in one session because of
+// this abort.
+// * Running tests 4-7 in isolation (skipping 1-3), Test 4 (sizeof on a
+// pre-existing type) produced
+// JIT session error: Symbols not found: [ _sz_known2 ]
+// error: Failed to materialize symbols: ...
+// followed by the process spinning at 100% CPU instead of returning to
+// the prompt for the remaining input (tests 5-7 never ran in that
+// session either).
+// * Running Test 1 completely on its own (no preceding chunks) did NOT
+// reproduce the wrong-value/duplicate-symbol failure -- it printed the
+// correct "dangling_name = 99". This is expected for a poisoned/reused-
+// memory bug: whether a stale pointer's target has been overwritten by
+// something that "looks wrong" depends on what the allocator handed out
+// to *other* code in between, so the manifestation is sensitive to the
+// exact preceding session history, not just to the isolated snippet.
+// That is precisely why this file chains many small scenarios in one
+// session rather than shipping them as independent one-liners.
+//
+// -----------------------------------------------------------------------
+// Background
+// -----------------------------------------------------------------------
+// Interpreter::Parse (clang/lib/Interpreter/Interpreter.cpp) wraps every
+// incremental input in a PTUSlabRollback guard:
+// 1. Takes CheckPoint = Ctx.getAllocator().checkPoint()
+// 2. Stashes ASTContext/Sema side-table sizes (ASTContextStateStash,
+// SemaStateStash)
+// 3. Runs IncrementalParser::Parse()
+// 4. On success: commits (keeps everything)
+// 5. On failure: SemaState.restore(), ASTCtxState.restore(), then
+// Ctx.getAllocator().restoreToCheckPoint(CheckPoint)
+//
+// restoreToCheckPoint (llvm/include/llvm/Support/Allocator.h) does not just
+// move a pointer back -- it actively memset()s the reclaimed range to 0xCD
+// ("poisonMemory") outside of ASan builds. So *any* pointer left behind in a
+// side-table that the two StateStash::restore() functions fail to clean up
+// is not merely suspect, it is guaranteed to point at either poisoned bytes
+// or, once new allocations reuse that space, at a completely unrelated
+// object (type confusion).
+//
+// Reading ASTContextStateStash.cpp and SemaStateStash.cpp shows the
+// restore() paths fall into three buckets:
+// (a) real cleanup: erase from the container based on
+// isAfterCheckpoint(ptr, SlabCP) [handles most Type folding sets]
+// (b) resize()/pop-back style cleanup on trailing-append vectors
+// [FunctionScopes, LateParsedInstantiations, SavedVTableUses, ...]
+// (c) `assert(oldSize == newSize)` with NO actual erase -- compiled away
+// entirely under NDEBUG, so the container is left holding dangling
+// pointers with no diagnostic at all in a release build
+// [StringLiteralCache, KeyFunctions-adjacent maps when the early
+// return below fires, MergedDecls, TemplateInstCallbacks,
+// PendingInstantiations, LateParsedTemplateMap, VTableUses/
+// VTablesUsed, ...]
+// Additionally, ASTContextStateStash::restore() opens with:
+// if (CP.TypesSize == Ctx.Types.size()) return;
+// which skips *all* of the above (including the folding sets that do have
+// real cleanup logic) whenever the number of interned Type nodes happens to
+// be unchanged -- even though many other caches (record layouts, key
+// functions, ...) can grow without interning a new Type.
+//
+// Every test below is built to land in one of these gaps. None of the
+// "poison" code paths depend on undefined behavior sanitizers to observe --
+// under a debug (assertions-enabled) build several of them should abort on
+// the assert() itself; under a release build the same inputs should
+// eventually crash, print garbage, or misbehave once the poisoned/reused
+// memory is dereferenced.
+// -----------------------------------------------------------------------
+
+extern "C" int printf(const char *, ...);
+
+// =======================================================================
+// Test 1: Sema::IdResolver keeps a dangling entry after a failed parse.
+//
+// SemaStateStash::restore() (clang/lib/Interpreter/SemaStateStash.cpp)
+// walks S.getCurScope()->decls() and calls Scope::RemoveDecl() for every
+// decl allocated after the checkpoint -- but the matching
+// `S.IdResolver.RemoveDecl(D)` call right above it is commented out. Name
+// lookup goes through IdResolver, not just Scope, so `dangling_name`'s
+// VarDecl stays "found" by lookup even though its storage is about to be
+// poisoned by the allocator rewind.
+//
+// `int dangling_name = 7;` fully binds (Scope + IdResolver + DeclContext
+// lookup map) before `intentional_error_type` is even parsed, because
+// IncrementalParser::ParseOrWrapTopLevelDecl only checks
+// Diags.hasErrorOccurred() *after* parsing every top-level decl in the
+// chunk. So both statements are discarded together, but only Scope forgets
+// about `dangling_name`.
+// =======================================================================
+int dangling_name = 7; intentional_error_type garbage_after_dangling_name;
+
+// Redeclare the same identifier in a fresh, successful chunk. If the stale
+// IdResolver entry survived, Sema's redeclaration-merging logic
+// (Sema::MergeVarDecl* et al.) may try to compare this new VarDecl against
+// the dangling one -- reading 0xCD-poisoned memory, or memory since reused
+// by an unrelated allocation.
+int dangling_name = 99;
+auto t1 = printf("dangling_name = %d\n", dangling_name);
+// CHECK: dangling_name = 99
+
+// =======================================================================
+// Test 2: Amplify test 1 by repeating the fail/redeclare cycle several
+// times on the same identifier. Because every failed chunk takes its
+// checkpoint at (almost) the same allocator offset, each failed attempt's
+// storage for `stress_var` gets reallocated over the *same* address range
+// poisoned by the previous attempt. Any stale IdResolver node left behind
+// by an earlier iteration therefore ends up aliasing whatever the *next*
+// iteration (or the final, successful declaration) allocates there --
+// classic type-confusion setup, and a good candidate to run under ASan.
+// =======================================================================
+int stress_var = 0; intentional_error_type e0;
+int stress_var = 1; intentional_error_type e1;
+int stress_var = 2; intentional_error_type e2;
+int stress_var = 3; intentional_error_type e3;
+int stress_var = 4; intentional_error_type e4;
+int stress_var = 42;
+auto t2 = printf("stress_var = %d\n", stress_var);
+// CHECK: stress_var = 42
+
+// =======================================================================
+// Test 3: Sema::PendingInstantiations does not survive a failed parse.
+//
+// ParseOrWrapTopLevelDecl() returns *before* calling
+// LocalInstantiations.perform()/GlobalInstantiations.perform() whenever
+// Diags.hasErrorOccurred() -- see the early `return` right after
+// CleanUpPTU() in IncrementalParser.cpp. So any instantiation work queued
+// while parsing `Boom<int> boom_instance; boom_instance.trigger();` is never
+// drained on this path. SemaStateStash::restore() only compares
+// S.PendingInstantiations.size() with an assert (it's a std::deque, never
+// resized/erased), so a stale PendingImplicitInstantiation entry pointing
+// at the (about-to-be-poisoned) `Boom<int>::trigger` specialization can
+// leak into whatever the *next* successful chunk's own eager-instantiation
+// pass processes.
+// =======================================================================
+template <typename T> struct Boom { void trigger() { T v; (void)v; } };
+Boom<int> boom_instance; boom_instance.trigger(); intentional_error_type boom_garbage;
+
+// This chunk's own GlobalEagerInstantiationScope/LocalEagerInstantiationScope
+// pass (constructed fresh in every ParseOrWrapTopLevelDecl call) is where a
+// leftover PendingInstantiations entry from the failed chunk above would
+// get processed against poisoned memory.
+int after_boom_marker = 1;
+auto t3 = printf("after_boom_marker = %d\n", after_boom_marker);
+// CHECK: after_boom_marker = 1
+
+// =======================================================================
+// Test 4: ASTContextStateStash's Types.size() early-return guard skips
+// cleanup of caches that don't depend on interning a new Type.
+//
+// void ASTContextStateStash::restore(...) {
+// if (CP.TypesSize == Ctx.Types.size())
+// return;
+// ... (all the real per-folding-set cleanup lives below this line) ...
+//
+// `AlreadyKnown` already exists (its RecordType was interned when it was
+// first declared, in the prior committed chunk). Computing sizeof() on it
+// only populates Ctx.ASTRecordLayouts (and, since it's a POD aggregate here,
+// nothing else) -- it does not intern a new Type, so Ctx.Types.size() is
+// unchanged across this failing chunk and the whole restore() body,
+// including ASTRecordLayouts's own (otherwise correct) erase-by-checkpoint
+// logic, is skipped.
+// =======================================================================
+struct AlreadyKnown { int a; int b; };
+unsigned long sz_known = sizeof(AlreadyKnown); intentional_error_type sizeof_garbage;
+
+// Recomputing sizeof() on the same, still-live type must not dereference a
+// stale ASTRecordLayout* left behind by the chunk above.
+unsigned long sz_known2 = sizeof(AlreadyKnown);
+auto t4 = printf("sizeof(AlreadyKnown) = %lu\n", sz_known2);
+// CHECK: sizeof(AlreadyKnown) = 8
+
+// =======================================================================
+// Test 5: Virtual dispatch bookkeeping (Sema::VTableUses/VTablesUsed,
+// ASTContext::KeyFunctions) is assert-only / guard-gated, same as above,
+// but exercised through polymorphic classes and `new` instead of sizeof.
+// RecordLayoutBuilder.cpp populates Ctx.KeyFunctions[RD] while laying out
+// `Derived` for the `new` expression below; Sema::MarkVTableUsed populates
+// VTableUses/VTablesUsed. Both are torn down on a *committed* chunk but
+// only assert-compared on a rolled-back one.
+// =======================================================================
+struct Base { virtual int val() { return 1; } virtual ~Base() {} };
+struct Derived : Base { int val() override { return 2; } };
+Base* bp = new Derived(); int vv = bp->val(); intentional_error_type vtable_garbage;
+
+// A fresh Base*/Derived pair exercising the same key-function/vtable-use
+// bookkeeping. If the discarded attempt above left stale KeyFunctions /
+// VTablesUsed state pointing at poisoned memory, codegen for *this* vtable
+// can be skipped, corrupted, or crash outright.
+Base* bp2 = new Derived();
+auto t5 = printf("val = %d\n", bp2->val());
+// CHECK: val = 2
+
+// =======================================================================
+// Test 6: Ctx.StringLiteralCache has no erase logic at all -- not gated by
+// the Types.size() guard (defining a brand-new function interns a new
+// FunctionProtoType, so the early return above does *not* fire here), just
+// a bare `assert(CP.StringLiteralCacheSize == Ctx.StringLiteralCache.size())`
+// with nothing to actually undo the insertion made by
+// ASTContext::getPredefinedStringLiteralFromCache when Sema processes
+// __PRETTY_FUNCTION__. In an assertions-enabled build this is the test most
+// likely to abort immediately and deterministically, independent of memory
+// poisoning, purely because the size check itself fails.
+// =======================================================================
+void uses_predefined_expr() { const char* pf = __PRETTY_FUNCTION__; (void)pf; } intentional_error_type predefined_garbage;
+
+// A second, differently-named function that also uses __PRETTY_FUNCTION__,
+// forcing another StringLiteralCache lookup/insert keyed differently from
+// the discarded one above.
+void uses_predefined_expr_again() { const char* pf = __PRETTY_FUNCTION__; (void)pf; }
+int pf_ran = 1;
+auto t6 = printf("pf_ran = %d\n", pf_ran);
+// CHECK: pf_ran = 1
+
+// =======================================================================
+// Test 7: Cross-declaration merging state (Ctx.MergedDecls,
+// Ctx.InstantiatedFromUsingShadowDecl) survives a failed using-declaration.
+// A `using` declaration creates a UsingShadowDecl tied back to the
+// original NS::helper via a side map that SemaStateStash/
+// ASTContextStateStash only assert-compare. If the failed attempt's
+// UsingShadowDecl (and its bookkeeping entry) is left dangling, a
+// subsequent successful `using NS::helper;` shares the same DeclarationName
+// and could resolve through, or conflict with, the stale shadow chain.
+// =======================================================================
+namespace NS { int helper() { return 10; } }
+using NS::helper; intentional_error_type merge_garbage;
+
+using NS::helper;
+auto t7 = printf("helper() = %d\n", helper());
+// CHECK: helper() = 10
+
+%quit
>From f0f8e889e8ae7000f2ee9fe8910979b55d280282 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 13 Sep 2026 14:34:46 +0530
Subject: [PATCH 03/13] Add Initial impl ErrorRecovery.h
---
clang/include/clang/AST/ASTContext.h | 19 +-
clang/include/clang/AST/ASTMutationListener.h | 6 +
clang/include/clang/AST/Decl.h | 6 +-
clang/include/clang/AST/DeclBase.h | 19 +-
clang/include/clang/AST/DeclCXX.h | 3 +
.../include/clang/AST/DeclContextInternals.h | 4 +-
clang/include/clang/AST/DeclTemplate.h | 6 +
clang/include/clang/AST/DeclarationName.h | 2 +
clang/include/clang/AST/RecordLayout.h | 4 +-
clang/include/clang/AST/Redeclarable.h | 4 +
.../include/clang/Interpreter/ErrorRecovery.h | 1404 ++++++++++++++++-
clang/include/clang/Sema/Sema.h | 4 +-
clang/lib/AST/ASTContext.cpp | 11 +-
clang/lib/AST/DeclBase.cpp | 3 -
clang/lib/Frontend/MultiplexConsumer.cpp | 5 +
.../lib/Interpreter/ASTContextStateStash.cpp | 461 +++++-
clang/lib/Interpreter/IncrementalAction.cpp | 12 +-
clang/lib/Interpreter/IncrementalAction.h | 4 +-
clang/lib/Interpreter/IncrementalParser.cpp | 11 +-
clang/lib/Interpreter/Interpreter.cpp | 10 +-
clang/lib/Interpreter/SemaStateStash.cpp | 78 +-
clang/test/Interpreter/ptu-rewind-stress.cpp | 171 +-
llvm/include/llvm/ADT/FoldingSet.h | 5 +
llvm/include/llvm/Support/Allocator.h | 24 +-
24 files changed, 1916 insertions(+), 360 deletions(-)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 01f83b4d85113..aaefe14fa828d 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -85,7 +85,7 @@ template <> struct DenseMapInfo<ScalableVecTyKey> {
namespace clang {
class APValue;
-class ASTContextStateStash;
+class ASTContextStateRecovery;
class ASTMutationListener;
class ASTRecordLayout;
class AtomicExpr;
@@ -235,11 +235,6 @@ struct QualTypeBoolInfo {
}
};
-struct TypeForDeclMutation {
- TypeDecl *Decl;
- const Type *OldValue;
-};
-
/// Holds long-lived AST nodes (such as types and decls) that can be
/// referred to throughout the semantic analysis of a file.
class ASTContext : public RefCountedBase<ASTContext> {
@@ -247,10 +242,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable SmallVector<Type *, 0> Types;
- bool IncrementalErrorRecoveryMode = true;
- mutable SmallVector<TypeForDeclMutation> PendingTypeForDeclMutations;
- mutable llvm::DenseSet<const DeclContext *> PendingDCMutations;
-
mutable llvm::FoldingSet<ExtQuals> ExtQualNodes;
mutable llvm::UniquingSet<ComplexType> ComplexTypes;
mutable llvm::UniquingSet<PointerType> PointerTypes{GeneralTypesLog2InitSize};
@@ -616,18 +607,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
using TemplateOrSpecializationInfo =
llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *>;
- bool isIncrementalErrorRecoveryMode() const {
- return IncrementalErrorRecoveryMode;
- }
-
private:
friend class ASTDeclReader;
friend class ASTReader;
friend class ASTWriter;
template <class> friend class serialization::AbstractTypeReader;
friend class CXXRecordDecl;
- // friend class IncrementalParser;
- friend class ASTContextStateStash;
+ friend class IncrementalParser;
+ friend class ASTContextStateRecovery;
/// A mapping to contain the template or declaration that
/// a variable declaration describes or was instantiated from,
diff --git a/clang/include/clang/AST/ASTMutationListener.h b/clang/include/clang/AST/ASTMutationListener.h
index 7540b7f90ee86..14cb3b1d14612 100644
--- a/clang/include/clang/AST/ASTMutationListener.h
+++ b/clang/include/clang/AST/ASTMutationListener.h
@@ -195,6 +195,12 @@ class ASTMutationListener {
virtual void AddedAnonymousNamespace(const TranslationUnitDecl *TU,
NamespaceDecl *AnonNamespace) {}
+ /// A TagDecl's cached TypeForDecl was materialized for the first time.
+ ///
+ /// \param TD The declaration whose type cache was populated.
+ /// \param T The Type now cached. Never null.
+ virtual void AddedTagDeclType(const TagDecl *TD, const Type *T) {}
+
// NOTE: If new methods are added they should also be added to
// MultiplexASTMutationListener.
};
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index d5b7f6240d134..05a102d584a57 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -57,6 +57,7 @@
namespace clang {
class ASTContext;
+class ASTStateReader;
struct ASTTemplateArgumentListInfo;
class CompoundStmt;
class DependentFunctionTemplateSpecializationInfo;
@@ -3644,13 +3645,11 @@ class IndirectFieldDecl : public ValueDecl,
static bool classofKind(Kind K) { return K == IndirectField; }
};
-class ASTContextStateStash;
-
/// Represents a declaration of a type.
class TypeDecl : public NamedDecl {
friend class ASTContext;
- friend class ASTContextStateStash;
friend class ASTReader;
+ friend class ASTStateReader;
/// This indicates the Type object that represents
/// this TypeDecl. It is a cache maintained by
@@ -3917,6 +3916,7 @@ class TagDecl : public TypeDecl,
public:
friend class ASTDeclReader;
+ friend class ASTStateReader;
friend class ASTDeclWriter;
using redecl_range = redeclarable_base::redecl_range;
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 9ec6f4c84a857..9dd1f60011ce0 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -43,10 +43,13 @@
namespace clang {
class ASTContext;
+class ASTContextStateRecovery;
+class ASTDeclDetacher;
class ASTMutationListener;
class Attr;
class BlockDecl;
class DeclContext;
+class DeclContextRepairer;
class ExternalSourceSymbolAttr;
class FunctionDecl;
class FunctionType;
@@ -76,8 +79,6 @@ enum AvailabilityResult {
AR_Unavailable
};
-class ASTContextStateStash;
-
/// Decl - This represents one declaration (or definition), e.g. a variable,
/// typedef, function, struct, etc.
///
@@ -260,7 +261,9 @@ class alignas(8) Decl {
private:
friend class DeclContext;
- friend class ASTContextStateStash;
+ // friend class ASTContextStateRecovery;
+ friend class ASTDeclDetacher;
+ friend class DeclContextRepairer;
struct MultipleDC {
DeclContext *SemanticDC;
@@ -1345,11 +1348,10 @@ namespace llvm {
}
namespace clang {
-class ASTContextStateStash;
/// A list storing NamedDecls in the lookup tables.
class DeclListNode {
friend class ASTContext; // allocate, deallocate nodes.
- friend class ASTContextStateStash;
+ friend class ASTContextStateRecovery;
friend class StoredDeclsList;
public:
using Decls = llvm::PointerUnion<NamedDecl*, DeclListNode*>;
@@ -1451,8 +1453,6 @@ enum class OMPDeclareReductionInitKind;
enum class ObjCImplementationControl;
enum class LinkageSpecLanguageIDs;
-class ASTContextStateStash;
-
/// DeclContext - This is used only as base class of specific decl types that
/// can act as declaration contexts. These decls are (only the top classes
/// that directly derive from DeclContext are mentioned, not their subclasses):
@@ -1471,7 +1471,10 @@ class ASTContextStateStash;
/// BlockDecl
/// CapturedDecl
class DeclContext {
- friend class ASTContextStateStash;
+ /// For restoring decl to its previous state (clang-repl error-recovery).
+ friend class ASTDeclDetacher;
+
+ friend class DeclContextRepairer;
/// For makeDeclVisibleInContextImpl
friend class ASTDeclReader;
/// For checking the new bits in the Serialization part.
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 1ebc8a7191bde..539cf28a76031 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -54,6 +54,8 @@
namespace clang {
class ASTContext;
+class ASTDeclDetacher;
+class ASTStateReader;
class ClassTemplateDecl;
class ConstructorUsingShadowDecl;
class CXXBasePath;
@@ -263,6 +265,7 @@ class CXXRecordDecl : public RecordDecl {
friend class ASTNodeImporter;
friend class ASTReader;
friend class ASTRecordWriter;
+ friend class ASTStateReader;
friend class ASTWriter;
friend class DeclContext;
friend class LambdaExpr;
diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h
index f753e1e8f9eb4..e7d9c2d742dc8 100644
--- a/clang/include/clang/AST/DeclContextInternals.h
+++ b/clang/include/clang/AST/DeclContextInternals.h
@@ -301,13 +301,13 @@ class StoredDeclsList {
}
};
-class ASTContextStateStash;
+class ASTContextStateRecovery;
class StoredDeclsMap
: public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> {
friend class ASTContext; // walks the chain deleting these
friend class DeclContext;
- friend class ASTContextStateStash;
+ friend class ASTContextStateRecovery;
llvm::PointerIntPair<StoredDeclsMap*, 1> Previous;
public:
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index d32b044ef40e2..a4beffb99465b 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -46,6 +46,8 @@
namespace clang {
+class ASTDeclDetacher;
+class ASTStateReader;
enum BuiltinTemplateKind : int;
class ClassTemplateDecl;
class ClassTemplatePartialSpecializationDecl;
@@ -824,7 +826,10 @@ class RedeclarableTemplateDecl : public TemplateDecl,
public:
friend class ASTDeclReader;
friend class ASTDeclWriter;
+ friend class ASTDeclDetacher;
friend class ASTReader;
+ friend class ASTStateReader;
+
template <class decl_type> friend class RedeclarableTemplate;
/// Retrieves the canonical declaration of this template.
@@ -2312,6 +2317,7 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
friend class ASTDeclReader;
friend class ASTDeclWriter;
+ friend class ASTStateReader;
friend class TemplateDeclInstantiator;
/// Load any lazily-loaded specializations from the external source.
diff --git a/clang/include/clang/AST/DeclarationName.h b/clang/include/clang/AST/DeclarationName.h
index 5121b561288c0..dd490c688de18 100644
--- a/clang/include/clang/AST/DeclarationName.h
+++ b/clang/include/clang/AST/DeclarationName.h
@@ -32,6 +32,7 @@
namespace clang {
class ASTContext;
+class ASTContextStateRecovery;
template <typename> class CanQual;
class DeclarationName;
class DeclarationNameTable;
@@ -591,6 +592,7 @@ inline bool operator>=(DeclarationName LHS, DeclarationName RHS) {
/// uniqued versions of each of the C++ special names, which can be
/// retrieved using its member functions (e.g., getCXXConstructorName).
class DeclarationNameTable {
+ friend class ASTContextStateRecovery;
/// Used to allocate elements in the FoldingSets below.
const ASTContext &Ctx;
diff --git a/clang/include/clang/AST/RecordLayout.h b/clang/include/clang/AST/RecordLayout.h
index 73ecb90fbcd88..a7964b69feb03 100644
--- a/clang/include/clang/AST/RecordLayout.h
+++ b/clang/include/clang/AST/RecordLayout.h
@@ -26,7 +26,7 @@
namespace clang {
class ASTContext;
-class ASTContextStateStash;
+class ASTContextStateRecovery;
class CXXRecordDecl;
/// ASTRecordLayout -
@@ -61,7 +61,7 @@ class ASTRecordLayout {
private:
friend class ASTContext;
- friend class ASTContextStateStash;
+ friend class ASTContextStateRecovery;
/// Size - Size of record in characters.
CharUnits Size;
diff --git a/clang/include/clang/AST/Redeclarable.h b/clang/include/clang/AST/Redeclarable.h
index fe2648aa58301..150c310530c03 100644
--- a/clang/include/clang/AST/Redeclarable.h
+++ b/clang/include/clang/AST/Redeclarable.h
@@ -25,7 +25,9 @@
namespace clang {
class ASTContext;
+class ASTDeclDetacher;
class Decl;
+class DeclContextRepairer;
// Some notes on redeclarables:
//
@@ -189,6 +191,8 @@ class Redeclarable {
friend class ASTDeclUnmerger;
friend class ASTDeclReader;
friend class ASTDeclWriter;
+ friend class ASTDeclDetacher;
+ friend class DeclContextRepairer;
friend class IncrementalParser;
Redeclarable(const ASTContext &Ctx)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 245d9416a8933..0c47e9bc2296b 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -13,11 +13,16 @@
#ifndef LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
#define LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTMutationListener.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
#include "clang/Sema/Sema.h"
+#include "llvm/ADT/DenseMap.h"
+
namespace clang {
class ASTContext;
-class Sema;
struct SemaStashCheckPoint {
/// Sema::*
@@ -98,18 +103,19 @@ struct SemaStashCheckPoint {
size_t CodeSynthesisContextLookupModulesSize = 0;
size_t LookupModulesCacheSize = 0;
size_t VisibleNamespaceCacheSize = 0;
- size_t TemplateInstCallbacksSize = 0;
+ // size_t TemplateInstCallbacksSize = 0;
size_t PendingInstantiationsSize = 0;
size_t LateParsedInstantiationsSize = 0;
size_t SavedVTableUsesSize = 0;
size_t SavedPendingInstantiationsSize = 0;
size_t PendingLocalImplicitInstantiationsSize = 0;
+ size_t CurrentCachedTemplateArgsSize = 0;
size_t UnsubstitutedConstraintSatisfactionCacheSize = 0;
size_t SubsumptionCacheSize = 0;
size_t NormalizationCacheSize = 0;
size_t SatisfactionCacheSize = 0;
size_t SatisfactionStackSize = 0;
-// size_t NullabilityMapSize = 0;
+ // size_t NullabilityMapSize = 0;
size_t DeclsWithEffectsToVerifySize = 0;
// size_t AllEffectsToVerifySize = 0;
};
@@ -118,12 +124,12 @@ struct SemaStashCheckPoint {
///
/// Usage:
/// SemaStashCheckPoint CP;
-/// SemaStateStash Stash(S);
+/// SemaStateRecovery Stash(S);
/// Stash.stash(CP);
/// // ... parse ...
/// if (failed)
/// Stash.restore(CP, ASTSlabCP);
-class SemaStateStash {
+class SemaStateRecovery {
Sema &S;
/// Holds full copies of PragmaStack, PragmaClangSection, FileNullabilityMap,
@@ -132,11 +138,8 @@ class SemaStateStash {
// std::unique_ptr<PragmaSnapshot> Pragmas;
public:
- explicit SemaStateStash(Sema &S) : S(S) {}
- // ~SemaStateStash();
-
- // SemaStateStash(const SemaStateStash &) = delete;
- // SemaStateStash &operator=(const SemaStateStash &) = delete;
+ explicit SemaStateRecovery(Sema &S) : S(S) {}
+ // ~SemaStateRecovery();
void stash(SemaStashCheckPoint &CP);
void restore(SemaStashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
@@ -243,7 +246,7 @@ struct StashCheckPoint {
size_t ArrayOperatorDeletesForVirtualDtorSize = 0;
size_t GlobalArrayOperatorDeletesForVirtualDtorSize = 0;
- size_t RequireVectorDeletingDtorSize = 0;
+ size_t MaybeRequireVectorDeletingDtorSize = 0;
size_t MergedDeclsSize = 0;
size_t DeclAttrsSize = 0;
@@ -255,7 +258,7 @@ struct StashCheckPoint {
size_t ScalableVecTyMapSize = 0;
size_t LambdaCastPathsSize = 0;
- size_t DeclRawCommentsSize = 0;
+ size_t RawCommentsSize = 0;
size_t RedeclChainCommentsSize = 0;
size_t CommentlessRedeclChainsSize = 0;
size_t ParsedCommentsSize = 0;
@@ -290,26 +293,1393 @@ struct StashCheckPoint {
mutable ObjCInterfaceDecl *ObjCProtocolClassDeclCP = nullptr;
// llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
-// = llvm::PointerIntPair<StoredDeclsMap *, 1>(nullptr, 0);
+ // = llvm::PointerIntPair<StoredDeclsMap *, 1>(nullptr, 0);
mutable QualType AutoDeductTy; // Deduction against 'auto'.
mutable QualType AutoRRefDeductTy; // Deduction against 'auto &&'.
// mutable DeclarationNameTable DeclarationNames; need to revert.
+ struct DeclarationNamesCP {
+ size_t CXXConstructorNamesSize = 0;
+ size_t CXXDestructorNamesSize = 0;
+ size_t CXXConversionFunctionNamesSize = 0;
+ detail::CXXOperatorIdName CXXOperatorNamesCP[NUM_OVERLOADED_OPERATORS];
+
+ size_t CXXLiteralOperatorNamesSize = 0;
+
+ size_t CXXDeductionGuideNamesSize = 0;
+ };
+
+ DeclarationNamesCP DeclarationNames;
};
-class ASTContextStateStash {
+class ASTContextStateRecovery {
private:
ASTContext &Ctx;
public:
- explicit ASTContextStateStash(ASTContext &Ctx) : Ctx(Ctx) {}
+ explicit ASTContextStateRecovery(ASTContext &Ctx) : Ctx(Ctx) {}
- ASTContextStateStash(const ASTContextStateStash &) = delete;
- ASTContextStateStash &operator=(const ASTContextStateStash &) = delete;
+ ASTContextStateRecovery(const ASTContextStateRecovery &) = delete;
+ ASTContextStateRecovery &operator=(const ASTContextStateRecovery &) = delete;
void stash(StashCheckPoint &CP);
void restore(StashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
void commit();
};
+
+/// Index of a per PTU State.
+using PTUID = unsigned;
+
+// A snapshot of the handful of DefinitionData bits/bitfields that can
+// change after a class (or class template specialization -- the two are
+// handled identically here since a specialization is non-dependent by the
+// time it's instantiated) is otherwise "done" being defined.
+//
+// These fields only move when the compiler generates implicit special
+// members.
+//
+// This footprint exists purely to undo the effects of implicit special
+// member generation.
+struct DefinitionDataFootprint {
+ unsigned Aggregate : 1;
+ unsigned PlainOldData : 1;
+ unsigned Empty : 1;
+ unsigned Polymorphic : 1;
+ unsigned IsStandardLayout : 1;
+ unsigned IsCXX11StandardLayout : 1;
+ unsigned HasTrivialSpecialMembers : 6;
+ unsigned HasTrivialSpecialMembersForCall : 6;
+ unsigned DeclaredNonTrivialSpecialMembers : 6;
+ unsigned DeclaredNonTrivialSpecialMembersForCall : 6;
+ unsigned HasIrrelevantDestructor : 1;
+ unsigned HasConstexprNonCopyMoveConstructor : 1;
+ unsigned HasDefaultedDefaultConstructor : 1;
+ unsigned HasConstexprDefaultConstructor : 1;
+ unsigned HasDeclaredCopyConstructorWithConstParam : 1;
+ unsigned HasDeclaredCopyAssignmentWithConstParam : 1;
+ unsigned IsAnyDestructorNoReturn : 1;
+ unsigned DeclaredSpecialMembers : 6;
+
+ bool operator==(const DefinitionDataFootprint &O) const {
+ return Aggregate == O.Aggregate && PlainOldData == O.PlainOldData &&
+ Empty == O.Empty && Polymorphic == O.Polymorphic &&
+ IsStandardLayout == O.IsStandardLayout &&
+ IsCXX11StandardLayout == O.IsCXX11StandardLayout &&
+ HasTrivialSpecialMembers == O.HasTrivialSpecialMembers &&
+ HasTrivialSpecialMembersForCall ==
+ O.HasTrivialSpecialMembersForCall &&
+ DeclaredNonTrivialSpecialMembers ==
+ O.DeclaredNonTrivialSpecialMembers &&
+ DeclaredNonTrivialSpecialMembersForCall ==
+ O.DeclaredNonTrivialSpecialMembersForCall &&
+ HasIrrelevantDestructor == O.HasIrrelevantDestructor &&
+ HasConstexprNonCopyMoveConstructor ==
+ O.HasConstexprNonCopyMoveConstructor &&
+ HasDefaultedDefaultConstructor == O.HasDefaultedDefaultConstructor &&
+ HasConstexprDefaultConstructor == O.HasConstexprDefaultConstructor &&
+ HasDeclaredCopyConstructorWithConstParam ==
+ O.HasDeclaredCopyConstructorWithConstParam &&
+ HasDeclaredCopyAssignmentWithConstParam ==
+ O.HasDeclaredCopyAssignmentWithConstParam &&
+ IsAnyDestructorNoReturn == O.IsAnyDestructorNoReturn &&
+ DeclaredSpecialMembers == O.DeclaredSpecialMembers;
+ }
+ bool operator!=(const DefinitionDataFootprint &O) const {
+ return !(*this == O);
+ }
+
+ // void update(const CXXRecordDecl::DefinitionData &Live) {
+ // Aggregate = Live.Aggregate;
+ // PlainOldData = Live.PlainOldData;
+ // Empty = Live.Empty;
+ // Polymorphic = Live.Polymorphic;
+ // IsStandardLayout = Live.IsStandardLayout;
+ // IsCXX11StandardLayout = Live.IsCXX11StandardLayout;
+ // HasTrivialSpecialMembers = Live.HasTrivialSpecialMembers;
+ // HasTrivialSpecialMembersForCall = Live.HasTrivialSpecialMembersForCall;
+ // DeclaredNonTrivialSpecialMembers = Live.DeclaredNonTrivialSpecialMembers;
+ // DeclaredNonTrivialSpecialMembersForCall =
+ // Live.DeclaredNonTrivialSpecialMembersForCall;
+ // HasIrrelevantDestructor = Live.HasIrrelevantDestructor;
+ // HasConstexprNonCopyMoveConstructor =
+ // Live.HasConstexprNonCopyMoveConstructor;
+ // HasDefaultedDefaultConstructor = Live.HasDefaultedDefaultConstructor;
+ // HasConstexprDefaultConstructor = Live.HasConstexprDefaultConstructor;
+ // HasDeclaredCopyConstructorWithConstParam =
+ // Live.HasDeclaredCopyConstructorWithConstParam;
+ // HasDeclaredCopyAssignmentWithConstParam =
+ // Live.HasDeclaredCopyAssignmentWithConstParam;
+ // IsAnyDestructorNoReturn = Live.IsAnyDestructorNoReturn;
+ // DeclaredSpecialMembers = Live.DeclaredSpecialMembers;
+ // }
+
+ // void restore(CXXRecordDecl::DefinitionData &Live) const {
+ // Live.Aggregate = Aggregate;
+ // Live.PlainOldData = PlainOldData;
+ // Live.Empty = Empty;
+ // Live.Polymorphic = Polymorphic;
+ // Live.IsStandardLayout = IsStandardLayout;
+ // Live.IsCXX11StandardLayout = IsCXX11StandardLayout;
+ // Live.HasTrivialSpecialMembers = HasTrivialSpecialMembers;
+ // Live.HasTrivialSpecialMembersForCall = HasTrivialSpecialMembersForCall;
+ // Live.DeclaredNonTrivialSpecialMembers = DeclaredNonTrivialSpecialMembers;
+ // Live.DeclaredNonTrivialSpecialMembersForCall =
+ // DeclaredNonTrivialSpecialMembersForCall;
+ // Live.HasIrrelevantDestructor = HasIrrelevantDestructor;
+ // Live.HasConstexprNonCopyMoveConstructor =
+ // HasConstexprNonCopyMoveConstructor;
+ // Live.HasDefaultedDefaultConstructor = HasDefaultedDefaultConstructor;
+ // Live.HasConstexprDefaultConstructor = HasConstexprDefaultConstructor;
+ // Live.HasDeclaredCopyConstructorWithConstParam =
+ // HasDeclaredCopyConstructorWithConstParam;
+ // Live.HasDeclaredCopyAssignmentWithConstParam =
+ // HasDeclaredCopyAssignmentWithConstParam;
+ // Live.IsAnyDestructorNoReturn = IsAnyDestructorNoReturn;
+ // Live.DeclaredSpecialMembers = DeclaredSpecialMembers;
+ // }
+};
+
+// Stores the state of a class template specialization. Tracks its
+// specialization kind, point of instantiation, source location, and lexical
+// declaration context so the state can be compared and restored.
+struct SpecializationFootprint {
+ unsigned SpecializationKind : 3;
+ SourceLocation PointOfInstantiation;
+ SourceLocation Location;
+ const DeclContext *LexicalDC;
+
+ bool operator==(const SpecializationFootprint &O) const {
+ return SpecializationKind == O.SpecializationKind &&
+ PointOfInstantiation == O.PointOfInstantiation &&
+ Location == O.Location && LexicalDC == O.LexicalDC;
+ }
+ bool operator!=(const SpecializationFootprint &O) const {
+ return !(*this == O);
+ }
+
+ void update(const ClassTemplateSpecializationDecl &Live) {
+ SpecializationKind = Live.getSpecializationKind();
+ PointOfInstantiation = Live.getPointOfInstantiation();
+ Location = Live.getLocation();
+ LexicalDC = Live.getLexicalDeclContext();
+ }
+
+ void restore(ClassTemplateSpecializationDecl &Live) const {
+ Live.setSpecializationKind(
+ static_cast<TemplateSpecializationKind>(SpecializationKind));
+ Live.setPointOfInstantiation(PointOfInstantiation);
+ Live.setLocation(Location);
+ Live.setLexicalDeclContext(const_cast<DeclContext *>(LexicalDC));
+ }
+};
+
+// Stores the state of a variable template specialization. Tracks its
+// specialization kind and point of instantiation so the state can be
+// compared and restored.
+struct VarSpecializationFootprint {
+ unsigned SpecializationKind : 3;
+ SourceLocation PointOfInstantiation;
+
+ bool operator==(const VarSpecializationFootprint &O) const {
+ return SpecializationKind == O.SpecializationKind &&
+ PointOfInstantiation == O.PointOfInstantiation;
+ }
+ bool operator!=(const VarSpecializationFootprint &O) const {
+ return !(*this == O);
+ }
+
+ void update(const VarTemplateSpecializationDecl &Live) {
+ SpecializationKind = Live.getSpecializationKind();
+ PointOfInstantiation = Live.getPointOfInstantiation();
+ }
+
+ void restore(VarTemplateSpecializationDecl &Live) const {
+ Live.setSpecializationKind(
+ static_cast<TemplateSpecializationKind>(SpecializationKind));
+ Live.setPointOfInstantiation(PointOfInstantiation);
+ }
+};
+
+// Stores the state of a function template specialization. Tracks its
+// specialization kind, point of instantiation, source location, lexical
+// declaration context, and constexpr state so the state can be compared
+// and restored.
+struct FunctionSpecializationFootprint {
+ unsigned SpecializationKind : 3;
+ SourceLocation PointOfInstantiation;
+ SourceLocation Location;
+ const DeclContext *LexicalDC;
+ ConstexprSpecKind ConstexprKind;
+
+ bool operator==(const FunctionSpecializationFootprint &O) const {
+ return SpecializationKind == O.SpecializationKind &&
+ PointOfInstantiation == O.PointOfInstantiation &&
+ Location == O.Location && LexicalDC == O.LexicalDC &&
+ ConstexprKind == O.ConstexprKind;
+ }
+ bool operator!=(const FunctionSpecializationFootprint &O) const {
+ return !(*this == O);
+ }
+
+ void update(const FunctionDecl &Live) {
+ SpecializationKind = Live.getTemplateSpecializationKind();
+ PointOfInstantiation = Live.getPointOfInstantiation();
+ Location = Live.getLocation();
+ LexicalDC = Live.getLexicalDeclContext();
+ ConstexprKind = Live.getConstexprKind();
+ }
+
+ void restore(FunctionDecl &Live) const {
+ Live.setTemplateSpecializationKind(
+ static_cast<TemplateSpecializationKind>(SpecializationKind),
+ PointOfInstantiation);
+ Live.setLocation(Location);
+ Live.setLexicalDeclContext(const_cast<DeclContext *>(LexicalDC));
+ Live.setConstexprKind(ConstexprKind);
+ }
+};
+
+// Stores the state of an ordinary class member created from a class template
+// instantiation. Tracks its specialization kind, point of instantiation, and
+// source location so the state can be compared and restored.
+struct MemberSpecializationFootprint {
+ unsigned SpecializationKind : 3;
+ SourceLocation PointOfInstantiation;
+ SourceLocation Location;
+
+ bool operator==(const MemberSpecializationFootprint &O) const {
+ return SpecializationKind == O.SpecializationKind &&
+ PointOfInstantiation == O.PointOfInstantiation &&
+ Location == O.Location;
+ }
+ bool operator!=(const MemberSpecializationFootprint &O) const {
+ return !(*this == O);
+ }
+
+ template <typename OwnerT> void update(const OwnerT &Live) {
+ const MemberSpecializationInfo *MSI = Live.getMemberSpecializationInfo();
+ SpecializationKind = MSI->getTemplateSpecializationKind();
+ PointOfInstantiation = MSI->getPointOfInstantiation();
+ Location = Live.getLocation();
+ }
+
+ template <typename OwnerT> void restore(OwnerT &Live) const {
+ MemberSpecializationInfo *MSI = Live.getMemberSpecializationInfo();
+ MSI->setTemplateSpecializationKind(
+ static_cast<TemplateSpecializationKind>(SpecializationKind));
+ MSI->setPointOfInstantiation(PointOfInstantiation);
+ Live.setLocation(Location);
+ }
+};
+
+class ASTStateReader {
+public:
+ static DefinitionDataFootprint *
+ createDefinitionDataFootprint(const ASTContext &Ctx, const CXXRecordDecl &RD);
+
+ static bool compareDefinitionDataFootprint(const DefinitionDataFootprint &FP,
+ const CXXRecordDecl &RD);
+
+ static void restoreDefinitionDataFootprint(const DefinitionDataFootprint &FP,
+ CXXRecordDecl &RD);
+
+ static SpecializationFootprint *
+ createSpecializationFootprint(const ASTContext &Ctx,
+ const ClassTemplateSpecializationDecl &Spec);
+
+ static bool
+ compareSpecializationFootprint(const SpecializationFootprint &FP,
+ const ClassTemplateSpecializationDecl &Spec) {
+ SpecializationFootprint Live;
+ Live.update(Spec);
+ return FP == Live;
+ }
+ static void
+ restoreSpecializationFootprint(const SpecializationFootprint &FP,
+ ClassTemplateSpecializationDecl &Spec) {
+ FP.restore(Spec);
+ }
+
+ static VarSpecializationFootprint *
+ createVarSpecializationFootprint(const ASTContext &Ctx,
+ const VarTemplateSpecializationDecl &Spec);
+
+ static bool
+ compareVarSpecializationFootprint(const VarSpecializationFootprint &FP,
+ const VarTemplateSpecializationDecl &Spec) {
+ VarSpecializationFootprint Live;
+ Live.update(Spec);
+ return FP == Live;
+ }
+ static void
+ restoreVarSpecializationFootprint(const VarSpecializationFootprint &FP,
+ VarTemplateSpecializationDecl &Spec) {
+ FP.restore(Spec);
+ }
+
+ static FunctionSpecializationFootprint *
+ createFunctionSpecializationFootprint(const ASTContext &Ctx,
+ const FunctionDecl &FD);
+
+ static bool compareFunctionSpecializationFootprint(
+ const FunctionSpecializationFootprint &FP, const FunctionDecl &FD);
+
+ static void restoreFunctionSpecializationFootprint(
+ const FunctionSpecializationFootprint &FP, FunctionDecl &FD) {
+ FP.restore(FD);
+ }
+
+ template <typename OwnerT>
+ static MemberSpecializationFootprint *
+ createMemberSpecializationFootprint(const ASTContext &Ctx, const OwnerT &D);
+
+ template <typename OwnerT>
+ static bool
+ compareMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
+ const OwnerT &D);
+ template <typename OwnerT>
+ static void
+ restoreMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
+ OwnerT &D);
+
+ static void restoreDefinitionAndRevertDC(CXXRecordDecl &RD);
+
+ static void revertDefinitionArrival(Decl &D);
+
+ static const void *getRawCommonPtr(const RedeclarableTemplateDecl &RT);
+
+ static bool isTemplateCanonInjectedTSTValid(const ClassTemplateDecl *CTD);
+
+ static void resetTemplateCommonBase(RedeclarableTemplateDecl &RT);
+
+ static void resetCanonInjectedTST(ClassTemplateDecl &CTD);
+
+ static const Type *getRawTypeForDecl(const TypeDecl *TD);
+
+ static void resetTypeForDecl(TypeDecl *TD);
+};
+
+template <typename DataT> struct Snapshot {
+ PTUID ID;
+ DataT *Data;
+};
+
+template <typename DataT> class StateAwareChain {
+ llvm::SmallVector<Snapshot<DataT>, 4> History;
+
+public:
+ bool empty() const { return History.empty(); }
+
+ const DataT *mostRecent() const {
+ return History.empty() ? nullptr : History.back().Data;
+ }
+
+ std::optional<PTUID> mostRecentID() const {
+ return History.empty() ? std::nullopt
+ : std::optional<PTUID>(History.back().ID);
+ }
+
+ std::optional<PTUID> oldestID() const {
+ return History.empty() ? std::nullopt
+ : std::optional<PTUID>(History.front().ID);
+ }
+
+ void commit(PTUID ID, DataT *Fresh) {
+ if (!History.empty() && *History.back().Data == *Fresh)
+ return;
+ assert(History.back().ID != ID);
+ History.push_back(Snapshot<DataT>{ID, Fresh});
+ }
+
+ const DataT *getPrevious(PTUID ID) const {
+ for (auto It = History.rbegin(); It != History.rend(); ++It)
+ if (It->ID < ID)
+ return It->Data;
+ return nullptr;
+ }
+
+ /// Pure removal: drop every entry with ID >= \p ID (LIFO). Never
+ /// restores anything -- callers must read mostRecent()/getPrevious()
+ /// themselves first if they need the value about to be dropped.
+ void removeFrom(PTUID ID) {
+ while (!History.empty() && History.back().ID >= ID)
+ History.pop_back(); // no delete.
+ }
+};
+
+// struct UnusedTrait {};
+
+using RecordDeclDefinitionDataChain = StateAwareChain<DefinitionDataFootprint>;
+using SpecializationChain = StateAwareChain<SpecializationFootprint>;
+using VarSpecializationChain = StateAwareChain<VarSpecializationFootprint>;
+using FunctionSpecializationChain =
+ StateAwareChain<FunctionSpecializationFootprint>;
+// One chain type shared by all four MemberSpecializationInfo-backed owner
+// kinds (Function/Var/Record/Enum) -- the footprint shape is identical
+// across all four (see MemberSpecializationFootprint's own comment), so
+// unlike the three chains above, a single alias is enough; what varies is
+// only the DenseMap key type in IncrementalStateTracker
+// (FunctionDecl*/VarDecl*/ CXXRecordDecl*/EnumDecl*), one map per owner kind.
+using MemberSpecializationChain =
+ StateAwareChain<MemberSpecializationFootprint>;
+
+class PTUCheckpointLedger {
+ llvm::SmallVector<llvm::SlabCheckPoint, 16> CheckpointBeforePTU;
+
+public:
+ /// Called once, right before parsing PTU \p ID begins.
+ void recordCheckpoint(PTUID ID, llvm::SlabCheckPoint CP) {
+ assert(ID == CheckpointBeforePTU.size() &&
+ "PTUs must be recorded in order");
+ CheckpointBeforePTU.push_back(CP);
+ }
+
+ /// \return the PTU that allocated \p Ptr, or std::nullopt if \p Ptr
+ /// predates the oldest recorded checkpoint.
+ ///
+ /// Walks newest-to-oldest: checkpoints only ever move forward for state
+ /// that has survived (committed PTUs are never rewound), so the first
+ /// checkpoint for which \p Ptr is "after" is the PTU that produced it.
+ std::optional<PTUID> attribute(const ASTContext &Ctx, const void *Ptr) const {
+ for (PTUID ID = CheckpointBeforePTU.size(); ID-- > 0;) {
+ if (Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]))
+ return ID;
+ }
+ return std::nullopt;
+ }
+
+ inline std::optional<PTUID> attributeByAddress(const ASTContext &Ctx,
+ const void *Ptr) {
+ // Binary search for the largest ID whose checkpoint Ptr is after -- i.e.
+ // the newest PTU boundary this address was allocated on or past.
+ size_t Lo = 0, Hi = CheckpointBeforePTU.size();
+ std::optional<PTUID> Result;
+ while (Lo < Hi) {
+ size_t Mid = Lo + (Hi - Lo) / 2;
+ if (Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[Mid])) {
+ Result = static_cast<PTUID>(Mid);
+ Lo = Mid + 1; // still after Mid's checkpoint -- look for a later one
+ } else {
+ Hi = Mid; // not even after Mid -- must be before it
+ }
+ }
+ return Result;
+ }
+
+ bool predatesPTU(const ASTContext &Ctx, const void *Ptr, PTUID ID) const {
+ assert(ID < CheckpointBeforePTU.size());
+ return !Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
+ }
+
+ bool isFromThisPTU(const ASTContext &Ctx, const void *Ptr, PTUID ID) const {
+ return Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
+ }
+
+ /// Drop checkpoints from \p ID onward.
+ void undoFrom(PTUID ID) {
+ if (ID < CheckpointBeforePTU.size())
+ CheckpointBeforePTU.resize(ID);
+ }
+};
+
+/// need to handle this TagDeclBitfields TagDeclBits;
+
+template <typename ValueT> struct FieldMutation {
+ PTUID ID;
+ ValueT OldValue;
+};
+
+template <typename OwnerT, typename ValueT> class FieldMutationChain {
+ llvm::DenseMap<const OwnerT *, llvm::SmallVector<FieldMutation<ValueT>, 2>>
+ Log;
+
+public:
+ void noteMutation(PTUID ID, const OwnerT *Owner, ValueT OldValue) {
+ auto &Entries = Log[Owner];
+ if (!Entries.empty() && Entries.back().ID == ID)
+ return;
+ Entries.push_back(FieldMutation<ValueT>{ID, OldValue});
+ }
+
+ const ValueT *mostRecent(const OwnerT *Owner) const {
+ auto It = Log.find(Owner);
+ return (It == Log.end() || It->second.empty())
+ ? nullptr
+ : &It->second.back().OldValue;
+ }
+
+ std::optional<PTUID> mostRecentID(const OwnerT *Owner) const {
+ auto It = Log.find(Owner);
+ if (It == Log.end())
+ return std::nullopt;
+ auto &History = It->second;
+ return History.empty() ? std::nullopt
+ : std::optional<PTUID>(History.back().ID);
+ }
+
+ std::optional<PTUID> oldestID(const OwnerT *Owner) const {
+ auto It = Log.find(Owner);
+ if (It == Log.end())
+ return std::nullopt;
+ auto &History = It->second;
+ return History.empty() ? std::nullopt
+ : std::optional<PTUID>(History.front().ID);
+ }
+
+ template <typename FnT> void forEachOwnerSince(PTUID ID, FnT &&Fn) const {
+ for (auto &Entry : Log)
+ if (!Entry.second.empty() && Entry.second.back().ID >= ID)
+ Fn(Entry.first);
+ }
+
+ void removeFrom(PTUID ID) {
+ llvm::SmallVector<OwnerT *> ToErase;
+ for (auto &Entry : Log) {
+ auto &Entries = Entry.second;
+ while (!Entries.empty() && Entries.back().ID >= ID)
+ Entries.pop_back();
+ if (Entries.empty())
+ ToErase.push_back(Entry.first);
+ }
+ for (const OwnerT *O : ToErase)
+ Log.erase(O);
+ }
+
+ void forget(const OwnerT *Owner) { Log.erase(Owner); }
+};
+
+// struct FunctionTypeEntry {
+// QualType Type;
+// QualType OverridingType;
+// };
+using FunctionExceptionSpecChain = FieldMutationChain<FunctionDecl, QualType>;
+using TypeForDeclChain = FieldMutationChain<TagDecl, const Type *>;
+// using CanonInjectedTSTChain =
+// FieldMutationChain<ClassTemplateDecl, CanQualType>;
+
+struct MutationRecord {
+ enum class DeclShape : uint16_t {
+ None = 0,
+ // Base shapes -- what the decl fundamentally IS. Exactly one is set.
+ Class = 1 << 0, // CXXRecordDecl/TagDecl: DefinitionData, TypeForDecl
+ Function = 1 << 1, // FunctionDecl: exception spec, deduced return, body
+ Var = 1 << 2, // VarDecl: cached constant-eval result
+ Enum = 1 << 3, // EnumDecl: TypeForDecl
+ Template = 1 << 4, // RedeclarableTemplateDecl: spec list grows
+ Typedef = 1 << 5, // TypedefDecl/TypeAliasDecl: TypeForDecl.
+ };
+
+ enum class MutationKind : uint32_t {
+ // ---- Common ----
+ DefinitionInstantiate = 1 << 0, // Class, Function, Var
+ SpecInfo = 1 << 1, // Class, Function, Var (Spec)
+ MemberSpecInfo = 1 << 2, // Class, Function, Var, Enum (Member)
+ TypeForDecl = 1 << 3, // Class, Enum
+
+ // ---- Shape-specific ----
+ DefinitionData = 1 << 8, // Class only
+ ExceptionSpec = 1 << 9, // Function only
+ DeducedReturnType = 1 << 10, // Function only
+ EvaluatedValue = 1 << 11, // Var only
+ SpecializationAdded = 1 << 12, // Template only
+
+ TemplateCommon = 1 << 4, // Template CommonBase
+ CanonInjectedTST = 1 << 5, // Template CommonBase Type
+ None = 1 << 24,
+ };
+
+ DeclShape S = DeclShape::None;
+ uint32_t MutationType = 0; // what this decl can ever have
+
+ void add(MutationKind K) { MutationType |= uint32_t(K); }
+ void add(uint32_t K) { MutationType |= uint32_t(K); }
+ bool has(MutationKind K) const { return MutationType & uint32_t(K); }
+ void clear(MutationKind K) { MutationType &= ~uint32_t(K); }
+};
+
+using DeclShape = MutationRecord::DeclShape;
+using MutationType = MutationRecord::MutationKind;
+
+class PTUMutationActions;
+
+struct PTUStateInfo {
+ PTUID ID;
+ const TranslationUnitDecl *ThisTU; // current info
+
+ llvm::MapVector<const Decl *, MutationRecord> Mutations;
+
+ // struct CreationRecord {
+ // DeclShape S;
+ // CreatedType Type;
+ // };
+
+ // llvm::MapVector<const Decl *, CreationRecord> CreatedDecls;
+ llvm::SmallPtrSet<const Decl *, 4> ImplicitDecls;
+
+ /// here touched info mean other this belongs to other PTUs;
+ llvm::SmallPtrSet<const DeclContext *, 4> TouchedDC;
+
+ template <typename KindT>
+ void noteMutated(const Decl *D, DeclShape S, KindT K) {
+ if (!D->isDefinedOutsideFunctionOrMethod())
+ return;
+ auto [It, Inserted] = Mutations.try_emplace(D);
+ if (Inserted) {
+ assert(It->second.S == S && "same decl noted under two different shapes");
+ It->second.S = S;
+ }
+ It->second.add(K);
+ }
+
+ void verifyMutations();
+
+ // False until this PTU is actually committed.
+ // PTUMutationActions uses this to decide what "undo" should do:
+ // - If this PTU was never committed, nothing was added to the chain,
+ // so we just restore the last PTU that was committed.
+ // - If this PTU was already committed (for example, by using %undo),
+ // first remove the entries created by this PTU, then restore the
+ // state from before this PTU was added.
+ bool Commited = false;
+};
+
+//===----------------------------------------------------------------------===//
+// SweepTracker -- Tracks declarations whose mutations cannot be reported
+// directly by an ASTMutationListener.
+//
+// Each Decl is tracked with the hidden mutation kinds that are still possible.
+// At commit time, sweep() checks the tracked kinds to find mutations made by
+// the current PTU. Once a mutation kind is confirmed or can no longer happen,
+// it is removed from tracking.
+//
+// During restore, a mutation kind can be tracked again so its mutation sites
+// can be restored when needed.
+//===----------------------------------------------------------------------===//
+class SweepTracker {
+ llvm::DenseMap<const Decl *, uint32_t> Active;
+
+public:
+ void track(const Decl *D, uint32_t HiddenBits) {
+ if (HiddenBits)
+ Active[D] |= HiddenBits;
+ }
+
+ bool isTrackedFor(const Decl *D, uint32_t Flag) const {
+ auto It = Active.find(D);
+ return It != Active.end() && (It->second & Flag);
+ }
+
+ void settle(const Decl *D, uint32_t Flag) {
+ auto It = Active.find(D);
+ if (It == Active.end())
+ return;
+ It->second &= ~Flag;
+ if (!It->second)
+ Active.erase(It);
+ }
+
+ // Call at commit(ID) time. OnConfirmed is invoked as
+ // (const Decl *D, DeclShape S, uint32_t ConfirmedKinds) for every decl
+ // that had something newly confirmed this sweep.
+ template <typename OnConfirmedFn>
+ void sweep(PTUID ID, OnConfirmedFn &&OnConfirmed);
+};
+
+//===----------------------------------------------------------------------===//
+// DeclLinkedState / LinkedDeclNodeGenerator
+//
+// These nodes keep the footprint-chain state associated with a Decl.
+// Each Decl kind has its own node type so the node only contains state that
+// is valid for that kind. DeclLinkedState then uses the node type to identify
+// which kind of Decl it belongs to.
+//
+// The generator owns these nodes and their chains and reuses them through
+// per-type free lists. This is intentional: nodes need to be released and
+// reused, so they use normal heap allocation instead of ASTContext's
+// bump allocator.
+//
+// The goal is to keep the linked state small, type-safe, and reusable without
+// adding fields for states that a particular Decl can never have.
+//===----------------------------------------------------------------------===//
+struct CXXClassDeclNode {
+ PTUID OriginID;
+ RecordDeclDefinitionDataChain *DefData = nullptr;
+ llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *> Spec;
+ CXXClassDeclNode *Next = nullptr; // free-list link, meaningless off the list
+};
+
+struct VarDeclNode {
+ PTUID OriginID;
+ llvm::PointerUnion<VarSpecializationChain *, MemberSpecializationChain *>
+ Spec;
+ VarDeclNode *Next = nullptr;
+};
+
+struct FunctionDeclNode {
+ PTUID OriginID;
+ llvm::PointerUnion<FunctionSpecializationChain *, MemberSpecializationChain *>
+ Spec;
+ FunctionDeclNode *Next = nullptr;
+};
+
+struct EnumDeclNode {
+ PTUID OriginID;
+ MemberSpecializationChain *MemberSpec =
+ nullptr; // the only thing an enum can ever have
+ EnumDeclNode *Next = nullptr;
+};
+
+using DeclLinkedState = llvm::PointerUnion<CXXClassDeclNode *, VarDeclNode *,
+ FunctionDeclNode *, EnumDeclNode *>;
+
+/// Intrusive free-list pool for the four node types above. Released nodes
+/// are kept for reuse instead of being immediately deleted. The pool is
+/// bounded by Capacity; once it is full, additional released nodes are
+/// deleted instead of being kept. This keeps memory usage bounded while
+/// still allowing freed nodes to be reused.
+
+template <typename T> class NodePool {
+ T *FreeList = nullptr;
+ unsigned FreeCount = 0;
+ unsigned Capacity;
+
+public:
+ explicit NodePool(unsigned Capacity = 64) : Capacity(Capacity) {}
+
+ ~NodePool() {
+ while (FreeList) {
+ T *Dead = FreeList;
+ FreeList = FreeList->Next;
+ delete Dead;
+ }
+ }
+ /// Hands back a reset (all-default) node -- recycled if one is free,
+ /// freshly allocated otherwise.
+ T *acquire() {
+ if (T *N = FreeList) {
+ FreeList = N->Next;
+ --FreeCount;
+ *N = T();
+ return N;
+ }
+ return new T();
+ }
+ /// Takes ownership back. Kept for a future acquire() to hand out again
+ /// if the free list is under capacity; reclaimed for real (delete)
+ /// otherwise, so this pool never holds more than Capacity dead nodes.
+ void release(T *N) {
+ if (FreeCount >= Capacity) {
+ delete N;
+ return;
+ }
+ N->Next = FreeList;
+ FreeList = N;
+ ++FreeCount;
+ }
+};
+
+/// Pool for reusing chain objects without modifying StateAwareChain.
+/// Released chains are kept for reuse, and the pool is bounded by Capacity
+/// so unused chains do not cause unbounded memory growth.
+template <typename ChainT> class ChainPool {
+ llvm::SmallVector<ChainT *, 8> Free;
+ unsigned Capacity;
+
+public:
+ explicit ChainPool(unsigned Capacity = 64) : Capacity(Capacity) {}
+
+ ~ChainPool() {
+ for (ChainT *C : Free)
+ delete C;
+ }
+
+ ChainT *acquire() {
+ if (!Free.empty()) {
+ ChainT *C = Free.pop_back_val();
+ *C = ChainT();
+ return C;
+ }
+ return new ChainT();
+ }
+ /// Same capacity rule as NodePool::release() -- reclaimed for real once
+ /// Free is at capacity, rather than growing without bound.
+ void release(ChainT *C) {
+ if (Free.size() >= Capacity) {
+ delete C;
+ return;
+ }
+ Free.push_back(C);
+ }
+};
+
+class LinkedDeclNodeGenerator {
+ NodePool<CXXClassDeclNode> RecordNodes;
+ NodePool<VarDeclNode> VarNodes;
+ NodePool<FunctionDeclNode> FunctionNodes;
+ NodePool<EnumDeclNode> EnumNodes;
+
+ ChainPool<RecordDeclDefinitionDataChain> DefDataChains;
+ ChainPool<SpecializationChain> ClassSpecChains;
+ ChainPool<VarSpecializationChain> VarSpecChains;
+ ChainPool<FunctionSpecializationChain> FunctionSpecChains;
+ ChainPool<MemberSpecializationChain> MemberSpecChains;
+
+public:
+ CXXClassDeclNode *acquireRecordNode() { return RecordNodes.acquire(); }
+ VarDeclNode *acquireVarNode() { return VarNodes.acquire(); }
+ FunctionDeclNode *acquireFunctionNode() { return FunctionNodes.acquire(); }
+ EnumDeclNode *acquireEnumNode() { return EnumNodes.acquire(); }
+
+ RecordDeclDefinitionDataChain *acquireDefDataChain() {
+ return DefDataChains.acquire();
+ }
+ SpecializationChain *acquireClassSpecChain() {
+ return ClassSpecChains.acquire();
+ }
+ VarSpecializationChain *acquireVarSpecChain() {
+ return VarSpecChains.acquire();
+ }
+ FunctionSpecializationChain *acquireFunctionSpecChain() {
+ return FunctionSpecChains.acquire();
+ }
+ MemberSpecializationChain *acquireMemberSpecChain() {
+ return MemberSpecChains.acquire();
+ }
+
+ // One release() name per type, dispatched by overload resolution --
+ // the caller doesn't need to know which internal pool a given pointer
+ // belongs to.
+ void release(CXXClassDeclNode *N) { RecordNodes.release(N); }
+ void release(VarDeclNode *N) { VarNodes.release(N); }
+ void release(FunctionDeclNode *N) { FunctionNodes.release(N); }
+ void release(EnumDeclNode *N) { EnumNodes.release(N); }
+ void release(RecordDeclDefinitionDataChain *C) { DefDataChains.release(C); }
+ void release(SpecializationChain *C) { ClassSpecChains.release(C); }
+ void release(VarSpecializationChain *C) { VarSpecChains.release(C); }
+ void release(FunctionSpecializationChain *C) {
+ FunctionSpecChains.release(C);
+ }
+ void release(MemberSpecializationChain *C) { MemberSpecChains.release(C); }
+};
+
+enum class LangMode : uint8_t {
+ C, // no DefinitionData, no templates, no implicit special members
+ CXX, // full set
+};
+
+// Overall flow:
+// - Track changes made by other PTUs so they can be applied to the relevant
+// chains/mutations when those PTUs are committed.
+// - On rollback of a committed PTU, remove the entries created by that PTU
+// and restore the state that existed before it.
+// - On rollback of a PTU that was never committed, restore the chain to its
+// current mostRecent() state, since this PTU never added anything to it.
+// - For the current PTU, collect newly added declarations that are
+// modifiable/visible to other PTUs. Local-only declarations are not exposed.
+//
+// In short, this keeps the shared declaration state in sync across PTUs while
+// keeping declarations that are local to the current PTU private.
+class IncrementalStateTracker {
+private:
+ ASTContext &Ctx;
+ LangMode Mode;
+ PTUID NextID = 0;
+ PTUID CurID = NextID;
+ PTUCheckpointLedger PTUSlabCheckpoints;
+
+ mutable llvm::DenseMap<PTUID, PTUStateInfo> PTUStateInfos;
+
+ // Stores all declaration footprint state in one map instead of maintaining
+ // separate maps for each declaration/specialization kind. The generator
+ // creates the appropriate node and chain for each Decl.
+ mutable LinkedDeclNodeGenerator Generator;
+ mutable llvm::DenseMap<const Decl *, DeclLinkedState> LinkedDecls;
+
+ // Tracks hidden mutations for all supported Decl kinds in one shared tracker,
+ // so they can be restored correctly during rollback.
+ SweepTracker HiddenMutationTracker;
+
+ FunctionExceptionSpecChain FunctionTypeMutations;
+ TypeForDeclChain TagdeclInfos;
+ // CanonInjectedTSTChain;
+
+ friend class PTUMutationActions;
+
+ SweepTracker &getHiddenMutationTracker() { return HiddenMutationTracker; }
+
+ /// Get-or-create the CXXClassDeclNode backing Canon, acquiring one from
+ /// the generator on first use. The only place a CXXClassDeclNode is
+ /// created for this map.
+ CXXClassDeclNode &recordNodeFor(const CXXRecordDecl *Canon) const {
+ DeclLinkedState &Info = LinkedDecls[Canon];
+ if (Info.isNull())
+ Info = Generator.acquireRecordNode();
+ return *cast<CXXClassDeclNode *>(Info);
+ }
+ VarDeclNode &varNodeFor(const VarDecl *Canon) const {
+ DeclLinkedState &Info = LinkedDecls[Canon];
+ if (Info.isNull())
+ Info = Generator.acquireVarNode();
+ return *cast<VarDeclNode *>(Info);
+ }
+ FunctionDeclNode &functionNodeFor(const FunctionDecl *Canon) const {
+ DeclLinkedState &Info = LinkedDecls[Canon];
+ if (Info.isNull())
+ Info = Generator.acquireFunctionNode();
+ return *cast<FunctionDeclNode *>(Info);
+ }
+ EnumDeclNode &enumNodeFor(const EnumDecl *Canon) const {
+ DeclLinkedState &Info = LinkedDecls[Canon];
+ if (Info.isNull())
+ Info = Generator.acquireEnumNode();
+ return *cast<EnumDeclNode *>(Info);
+ }
+
+ RecordDeclDefinitionDataChain &chainFor(const CXXRecordDecl *RD) {
+ CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+ if (!Node.DefData)
+ Node.DefData = Generator.acquireDefDataChain();
+ return *Node.DefData;
+ }
+
+ SpecializationChain &chainFor(const ClassTemplateSpecializationDecl *Spec) {
+ CXXClassDeclNode &Node = recordNodeFor(
+ cast<ClassTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
+ auto *Chain = Node.Spec.dyn_cast<SpecializationChain *>();
+ if (!Chain) {
+ Chain = Generator.acquireClassSpecChain();
+ Node.Spec = Chain;
+ }
+ return *Chain;
+ }
+
+ VarSpecializationChain &chainFor(const VarTemplateSpecializationDecl *Spec) {
+ VarDeclNode &Node = varNodeFor(
+ cast<VarTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
+ auto *Chain = Node.Spec.dyn_cast<VarSpecializationChain *>();
+ if (!Chain) {
+ Chain = Generator.acquireVarSpecChain();
+ Node.Spec = Chain;
+ }
+ return *Chain;
+ }
+
+ FunctionSpecializationChain &chainFor(const FunctionDecl *FD) {
+ FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
+ auto *Chain = Node.Spec.dyn_cast<FunctionSpecializationChain *>();
+ if (!Chain) {
+ Chain = Generator.acquireFunctionSpecChain();
+ Node.Spec = Chain;
+ }
+ return *Chain;
+ }
+
+ MemberSpecializationChain &memberSpecChainFor(const FunctionDecl *FD) {
+ FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
+ auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
+ if (!Chain) {
+ Chain = Generator.acquireMemberSpecChain();
+ Node.Spec = Chain;
+ }
+ return *Chain;
+ }
+ MemberSpecializationChain &memberSpecChainFor(const VarDecl *VD) {
+ VarDeclNode &Node = varNodeFor(VD->getCanonicalDecl());
+ auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
+ if (!Chain) {
+ Chain = Generator.acquireMemberSpecChain();
+ Node.Spec = Chain;
+ }
+ return *Chain;
+ }
+ MemberSpecializationChain &memberSpecChainFor(const CXXRecordDecl *RD) {
+ CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+ auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
+ if (!Chain) {
+ Chain = Generator.acquireMemberSpecChain();
+ Node.Spec = Chain;
+ }
+ return *Chain;
+ }
+ MemberSpecializationChain &memberSpecChainFor(const EnumDecl *ED) {
+ EnumDeclNode &Node = enumNodeFor(ED->getCanonicalDecl());
+ if (!Node.MemberSpec)
+ Node.MemberSpec = Generator.acquireMemberSpecChain();
+ return *Node.MemberSpec;
+ }
+
+ RecordDeclDefinitionDataChain *getChainFor(const CXXRecordDecl *RD) const {
+ CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+ return Node.DefData;
+ }
+
+ SpecializationChain *
+ getChainFor(const ClassTemplateSpecializationDecl *Spec) const {
+ CXXClassDeclNode &Node = recordNodeFor(
+ cast<ClassTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
+ return Node.Spec.dyn_cast<SpecializationChain *>();
+ }
+
+ VarSpecializationChain *
+ getChainFor(const VarTemplateSpecializationDecl *Spec) const {
+ VarDeclNode &Node = varNodeFor(
+ cast<VarTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
+ return Node.Spec.dyn_cast<VarSpecializationChain *>();
+ }
+
+ FunctionSpecializationChain *getChainFor(const FunctionDecl *FD) const {
+ FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
+ return Node.Spec.dyn_cast<FunctionSpecializationChain *>();
+ }
+
+ MemberSpecializationChain *
+ getMemberSpecChainFor(const FunctionDecl *FD) const {
+ FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
+ return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+ }
+ MemberSpecializationChain *getMemberSpecChainFor(const VarDecl *VD) const {
+ VarDeclNode &Node = varNodeFor(VD->getCanonicalDecl());
+ return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+ }
+ MemberSpecializationChain *
+ getMemberSpecChainFor(const CXXRecordDecl *RD) const {
+ CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+ return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+ }
+ MemberSpecializationChain *getMemberSpecChainFor(const EnumDecl *ED) const {
+ EnumDeclNode &Node = enumNodeFor(ED->getCanonicalDecl());
+ return Node.MemberSpec;
+ }
+
+public:
+ IncrementalStateTracker(ASTContext &Ctx, LangMode M) : Ctx(Ctx), Mode(M) {}
+
+ void beginPTU(llvm::SlabCheckPoint CP) {
+ CurID = NextID;
+ PTUSlabCheckpoints.recordCheckpoint(CurID, CP);
+ PTUStateInfos.try_emplace(CurID, PTUStateInfo{CurID});
+ ++NextID;
+ }
+
+ PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
+ bool isFromThisPTU(const void *Ptr, PTUID ID) {
+ return PTUSlabCheckpoints.isFromThisPTU(Ctx, Ptr, ID);
+ }
+
+ PTUStateInfo ¤t() const {
+ assert(NextID > 0 && "no PTU has been started");
+ auto It = PTUStateInfos.find(CurID);
+ assert(It != PTUStateInfos.end());
+ return It->second;
+ }
+
+ // Pops this PTU's own tail entry (if any) from *Field. If the chain is
+ // now empty -- meaning everything it ever held belonged to the PTU being
+ // rolled back -- releases it back to its pool and nulls the pointer.
+ // Returns whether the chain still has surviving (earlier-PTU) history.
+ template <typename ChainT>
+ static bool rollbackChainField(ChainT *&Field, PTUID ID,
+ LinkedDeclNodeGenerator &Gen);
+
+ static bool rollbackRecordSpec(
+ llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *>
+ &Spec,
+ PTUID ID, LinkedDeclNodeGenerator &Gen);
+ void removeLinkedDecl(const Decl *D, PTUID ID);
+ // just only remove entries from current();
+ void undoLastEntries();
+};
+
+class PTUMutationActions {
+private:
+ IncrementalStateTracker &Tracker;
+
+public:
+ explicit PTUMutationActions(IncrementalStateTracker &Tracker)
+ : Tracker(Tracker) {}
+
+ /// Every mutation this PTU recorded for a class-shaped decl, applied in
+ /// dependency order: definition data first (later steps read the completed
+ /// definition), then the type cache, then specialization footprints, then
+ /// lazily-completed members last (they can add members that the steps above
+ /// would otherwise have missed).
+ ///
+ /// Kinds are a bitmask, not alternatives -- one class can legitimately have
+ /// several set in a single PTU (e.g. a specialization that also had its
+ /// definition data completed), so these are sequential checks, not a switch.
+ void commitClassFamily(PTUID ID, const CXXRecordDecl *RD, MutationRecord &Rec,
+ bool IsNew);
+
+ /// Every mutation this PTU recorded for a function-shaped decl, or -- when
+ /// CR is non-null -- the baseline seed for one this PTU created.
+ ///
+ /// Kinds are a bitmask, not alternatives: one function can have its
+ /// exception spec resolved AND its body instantiated in the same PTU, so
+ /// these are sequential checks rather than a switch.
+ ///
+ /// Order matters: type-affecting mutations (exception spec, deduced return)
+ /// come first because the specialization footprint below reads the
+ /// function's type; body instantiation comes last because it can only
+ /// happen once everything about the signature is settled.
+ void commitFunctionFamily(PTUID ID, const FunctionDecl *FD,
+ MutationRecord &Rec, bool IsNew);
+
+ /// Every mutation this PTU recorded for a var-shaped decl, or -- when CR is
+ /// non-null -- the baseline seed for one this PTU created.
+ ///
+ /// Ordering: initializer instantiation first (it produces the expression
+ /// that constant evaluation later consumes), then the cached evaluated
+ /// value, then specialization/member footprints which read both.
+ void commitVarFamily(PTUID ID, const VarDecl *VD, MutationRecord &Rec,
+ bool IsNew);
+
+ /// Every mutation this PTU recorded for an enum-shaped decl, or -- when CR
+ /// is non-null -- the baseline seed for one this PTU created.
+ ///
+ /// The smallest family: enums have no specialization category (there is no
+ /// such thing as an enum template), no deferred bodies, and no members with
+ /// independent mutable state -- EnumConstantDecls live and die with the
+ /// EnumDecl, so they are not separately tracked.
+ void commitEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec,
+ bool IsNew);
+
+ // static DeclShape classifyShape(const Decl *D);
+
+ // class DeclStateCommitProxy;
+
+ // class DeclStateRestoreProxy;
+
+ template <typename DeclStateProxyT>
+ void walkDecls(const DeclContext *DC, DeclStateProxyT &Proxy);
+
+ // static uint32_t classifyPossibleKinds(DeclShape S, const Decl *D);
+
+ /// Given a Decl already known to be DeclShape S with FlaggedKinds set
+ /// (more than one bit at once is the normal case, not an edge case --
+ /// a single FunctionDecl can be MSI-backed AND have an unresolved
+ /// exception spec at the same time), returns the subset of
+ /// FlaggedKinds that actually differ from the last recorded snapshot.
+ /// Real chain access, not a guess: builds a fresh footprint the same
+ /// way commit() does and compares it against chainFor/
+ /// memberSpecChainFor's mostRecent() via the same operator== every
+ /// StateAwareChain already uses for its own dedup. A bit with no
+ /// chain anywhere in this file (DeducedReturnType has none -- see its
+ /// own listener override's comment; TypeForDeclChanged/
+ /// EvaluatedValueCached likewise) is never returned as verified --
+ /// there is nothing to compare it against, so it cannot be confirmed,
+ /// full stop, not assumed either way.
+ // static uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds,
+ // PTUID ID);
+
+ void commitLevel1(PTUID ID, const Decl *D, MutationRecord &Rec,
+ bool IsNew = false);
+
+ // respect the LIFO so only current inside map not commited can be commited
+ // not randon PTUID
+ // global map info shouldn't be commited before only added here. not note*
+ // time.
+ void commit(TranslationUnitDecl *MostRecentTU);
+
+ void restoreClassFamily(PTUID ID, const CXXRecordDecl *RD,
+ MutationRecord &Rec);
+
+ void restoreFunctionFamily(PTUID ID, const FunctionDecl *FD,
+ MutationRecord &Rec);
+
+ void restoreTemplateFamily(PTUID ID, const RedeclarableTemplateDecl *TD,
+ MutationRecord &Rec);
+
+ void restoreVarFamily(PTUID ID, const VarDecl *VD, MutationRecord &Rec);
+
+ /// Every mutation this PTU recorded for an enum-shaped decl, or -- when CR
+ /// is non-null -- the baseline seed for one this PTU created.
+ ///
+ /// The smallest family: enums have no specialization category (there is no
+ /// such thing as an enum template), no deferred bodies, and no members with
+ /// independent mutable state -- EnumConstantDecls live and die with the
+ /// EnumDecl, so they are not separately tracked.
+ void restoreEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec);
+
+ void restoreLevel1(PTUID ID, const Decl *D, MutationRecord &Rec);
+
+ void rollback(TranslationUnitDecl *MostRecentTU);
+};
+
+class PTUMutationRecorder : public ASTMutationListener {
+private:
+ IncrementalStateTracker &Tracker;
+
+ template <typename TemplateT, typename SpecT>
+ void noteTemplateDeclMutation(const TemplateT *TD, const SpecT *Spec,
+ DeclShape S) {}
+
+ void noteExceptionSpecMutation(const FunctionDecl *FD,
+ MutationType K = MutationType::ExceptionSpec) {
+ }
+
+ void noteDefinitionInstantiated(const Decl *D) {}
+
+public:
+ PTUMutationRecorder(IncrementalStateTracker &Tracker) : Tracker(Tracker) {}
+
+ /// A new TagDecl definition was completed.
+ void CompletedTagDefinition(const TagDecl *D) override {}
+
+ /// A new declaration with name has been added to a DeclContext.
+ void AddedVisibleDecl(const DeclContext *DC, const Decl *D) override {}
+
+ /// An implicit member was added after the definition was completed.
+ void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) override {
+ }
+
+ /// A template specialization (or partial one) was added to the
+ /// template declaration.
+ void AddedCXXTemplateSpecialization(
+ const ClassTemplateDecl *TD,
+ const ClassTemplateSpecializationDecl *D) override {}
+
+ /// A template specialization (or partial one) was added to the
+ /// template declaration.
+ void AddedCXXTemplateSpecialization(
+ const VarTemplateDecl *TD,
+ const VarTemplateSpecializationDecl *D) override {}
+
+ /// A template specialization (or partial one) was added to the
+ /// template declaration.
+ void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD,
+ const FunctionDecl *D) override {}
+
+ /// A function's exception specification has been evaluated or
+ /// instantiated.
+ void ResolvedExceptionSpec(const FunctionDecl *FD) override {}
+
+ /// A function's return type has been deduced.
+ void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override {
+ }
+
+ /// A virtual destructor's operator delete has been resolved.
+ void ResolvedOperatorDelete(const CXXDestructorDecl *DD,
+ const FunctionDecl *Delete,
+ Expr *ThisArg) override {}
+
+ /// A virtual destructor's operator global delete has been resolved.
+ void ResolvedOperatorGlobDelete(const CXXDestructorDecl *DD,
+ const FunctionDecl *GlobDelete) override {}
+
+ /// A virtual destructor's operator array delete has been resolved.
+ void ResolvedOperatorArrayDelete(const CXXDestructorDecl *DD,
+ const FunctionDecl *ArrayDelete) override {}
+
+ /// A virtual destructor's operator global array delete has been resolved.
+ void ResolvedOperatorGlobArrayDelete(
+ const CXXDestructorDecl *DD,
+ const FunctionDecl *GlobArrayDelete) override {}
+
+ /// An implicit member got a definition.
+ void CompletedImplicitDefinition(const FunctionDecl *D) override {
+ noteDefinitionInstantiated(D);
+ }
+
+ /// The instantiation of a templated function or variable was
+ /// requested. In particular, the point of instantiation and template
+ /// specialization kind of \p D may have changed.
+ void InstantiationRequested(const ValueDecl *D) override {}
+
+ /// A templated variable's definition was implicitly instantiated.
+ void VariableDefinitionInstantiated(const VarDecl *D) override {
+ noteDefinitionInstantiated(D);
+ // handle Memberinfo
+ }
+
+ /// A function template's definition was instantiated.
+ void FunctionDefinitionInstantiated(const FunctionDecl *D) override {
+ noteDefinitionInstantiated(D);
+ // handle Memberinfo
+ }
+
+ /// A default argument was instantiated.
+ void DefaultArgumentInstantiated(const ParmVarDecl *D) override {}
+
+ /// A default member initializer was instantiated.
+ void DefaultMemberInitializerInstantiated(const FieldDecl *D) override {}
+
+ /// A new objc category class was added for an interface.
+ void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD,
+ const ObjCInterfaceDecl *IFD) override {}
+
+ /// A declaration is marked used which was not previously marked used.
+ ///
+ /// \param D the declaration marked used
+ void DeclarationMarkedUsed(const Decl *D) override {}
+
+ /// A declaration is marked as OpenMP threadprivate which was not
+ /// previously marked as threadprivate.
+ ///
+ /// \param D the declaration marked OpenMP threadprivate.
+ void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override {}
+
+ /// A declaration is marked as OpenMP groupprivate which was not
+ /// previously marked as groupprivate.
+ ///
+ /// \param D the declaration marked OpenMP groupprivate.
+ void DeclarationMarkedOpenMPGroupPrivate(const Decl *D) override {}
+
+ /// A declaration is marked as OpenMP declaretarget which was not
+ /// previously marked as declaretarget.
+ ///
+ /// \param D the declaration marked OpenMP declaretarget.
+ /// \param Attr the added attribute.
+ void DeclarationMarkedOpenMPDeclareTarget(const Decl *D,
+ const Attr *Attr) override {}
+
+ /// A declaration is marked as a variable with OpenMP allocator.
+ ///
+ /// \param D the declaration marked as a variable with OpenMP allocator.
+ void DeclarationMarkedOpenMPAllocate(const Decl *D, const Attr *A) override {}
+
+ /// A declaration is marked as an OpenMP indirect call target.
+ ///
+ /// \param D the declaration marked as an indirect call target.
+ void DeclarationMarkedOpenMPIndirectCall(const Decl *D) override {}
+
+ /// A definition has been made visible by being redefined locally.
+ ///
+ /// \param D The definition that was previously not visible.
+ /// \param M The containing module in which the definition was made visible,
+ /// if any.
+ void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override {}
+
+ /// An attribute was added to a RecordDecl
+ ///
+ /// \param Attr The attribute that was added to the Record
+ ///
+ /// \param Record The RecordDecl that got a new attribute
+ void AddedAttributeToRecord(const Attr *Attr,
+ const RecordDecl *Record) override {}
+
+ /// An mangling number was added to a Decl
+ ///
+ /// \param D The decl that got a mangling number
+ ///
+ /// \param Number The mangling number that was added to the Decl
+ void AddedManglingNumber(const Decl *D, unsigned Number) override {}
+
+ /// An static local number was added to a Decl
+ ///
+ /// \param D The decl that got a static local number
+ ///
+ /// \param Number The static local number that was added to the Decl
+ void AddedStaticLocalNumbers(const Decl *D, unsigned Number) override {}
+
+ /// An anonymous namespace was added the translation unit decl
+ ///
+ /// \param TU The translation unit decl that got a new anonymous namespace
+ ///
+ /// \param AnonNamespace The anonymous namespace that was added
+ void AddedAnonymousNamespace(const TranslationUnitDecl *TU,
+ NamespaceDecl *AnonNamespace) override {}
+
+ void AddedTagDeclType(const TagDecl *TD, const Type *T) override {}
+};
} // end namespace clang
#endif // LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index feb1b9eea5c7b..7e20dbf371b20 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -186,7 +186,7 @@ class SemaSystemZ;
class SemaWasm;
class SemaX86;
class StandardConversionSequence;
-class SemaStateStash;
+class SemaStateRecovery;
class TemplateArgument;
class TemplateArgumentLoc;
class TemplateInstantiationCallback;
@@ -1588,7 +1588,7 @@ class Sema final : public SemaBase {
friend class ASTReader;
friend class ASTDeclReader;
friend class ASTWriter;
- friend class SemaStateStash;
+ friend class SemaStateRecovery;
private:
std::optional<std::unique_ptr<DarwinSDKInfo>> CachedDarwinSDKInfo;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index e850e9a0b61d1..f0d760845d3b4 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5596,6 +5596,10 @@ CanQualType ASTContext::getCanonicalTagType(const TagDecl *TD) const {
/*OwnsTag=*/false, /*IsInjected=*/false, /*CanonicalType=*/nullptr,
/*WithFoldingSetNode=*/false);
TD->TypeForDecl = CanonicalType;
+
+ if (ASTMutationListener *L = getASTMutationListener())
+ L->AddedTagDeclType(TD, TD->TypeForDecl);
+
return CanQualType::CreateUnsafe(QualType(CanonicalType, 0));
}
@@ -5622,10 +5626,11 @@ QualType ASTContext::getTagType(ElaboratedTypeKeyword Keyword,
/*OwnsTag=*/false, IsInjected, CanonicalType,
/*WithFoldingSetNode=*/false);
- if (IncrementalErrorRecoveryMode)
- PendingTypeForDeclMutations.push_back({const_cast<TagDecl *>(TD), TD->TypeForDecl});
-
TD->TypeForDecl = T;
+
+ if (ASTMutationListener *L = getASTMutationListener())
+ L->AddedTagDeclType(TD, TD->TypeForDecl);
+
return QualType(T, 0);
}
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 4bc4b8ca2acdc..548643cea2cce 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -2211,9 +2211,6 @@ void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D, bool Internal) {
}
DeclNameEntries.addOrReplaceDecl(D);
-
- if (getParentASTContext().isIncrementalErrorRecoveryMode())
- getParentASTContext().PendingDCMutations.insert(this);
}
UsingDirectiveDecl *DeclContext::udir_iterator::operator*() const {
diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp
index 4d27b04248f93..bd9f4af9b7fd2 100644
--- a/clang/lib/Frontend/MultiplexConsumer.cpp
+++ b/clang/lib/Frontend/MultiplexConsumer.cpp
@@ -130,6 +130,7 @@ class MultiplexASTMutationListener : public ASTMutationListener {
void AddedStaticLocalNumbers(const Decl *D, unsigned) override;
void AddedAnonymousNamespace(const TranslationUnitDecl *,
NamespaceDecl *AnonNamespace) override;
+ void AddedTagDeclType(const TagDecl *TD, const Type *T) override;
private:
std::vector<ASTMutationListener*> Listeners;
@@ -278,6 +279,10 @@ void MultiplexASTMutationListener::AddedAnonymousNamespace(
for (auto *L : Listeners)
L->AddedAnonymousNamespace(TU, AnonNamespace);
}
+void MultiplexASTMutationListener::AddedTagDeclType(const TagDecl *TD, const Type *T) {
+ for (auto *L : Listeners)
+ L->AddedTagDeclType(TD, T);
+}
} // end namespace clang
diff --git a/clang/lib/Interpreter/ASTContextStateStash.cpp b/clang/lib/Interpreter/ASTContextStateStash.cpp
index e25c67c46aa01..d9da3f7448da8 100644
--- a/clang/lib/Interpreter/ASTContextStateStash.cpp
+++ b/clang/lib/Interpreter/ASTContextStateStash.cpp
@@ -1,4 +1,4 @@
-//===--- ASTContextStateStash.cpp - ASTContext persistent state stash/restore
+//===--- ASTContextStateRecovery.cpp - ASTContext persistent state stash/restore
//----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -29,6 +29,29 @@
namespace clang {
/// Erases nodes from a FoldingSet based on a predicate.
+template <typename EntryType, typename PredT>
+static void eraseFoldingSetIf(llvm::UniquingSet<EntryType> &FS, PredT &&Pred) {
+ SmallVector<EntryType *, 16> ToRemove;
+ for (auto &N : FS)
+ if (Pred(N))
+ ToRemove.push_back(&N);
+
+ for (auto *N : ToRemove)
+ FS.erase(N);
+}
+
+template <typename EntryType, typename UniquingSetInfo, typename PredT>
+static void eraseFoldingSetIf(llvm::UniquingSet<EntryType, UniquingSetInfo> &FS,
+ PredT &&Pred) {
+ SmallVector<EntryType *, 16> ToRemove;
+ for (auto &N : FS)
+ if (Pred(N))
+ ToRemove.push_back(&N);
+
+ for (auto *N : ToRemove)
+ FS.erase(N);
+}
+
template <typename EntryType, typename PredT>
static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
SmallVector<EntryType *, 16> ToRemove;
@@ -37,7 +60,7 @@ static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
ToRemove.push_back(&N);
for (auto *N : ToRemove)
- FS.RemoveNode(N);
+ FS.erase(N);
}
template <typename EntryType, typename PredT>
@@ -51,7 +74,7 @@ eraseFoldingSetIf(llvm::ContextualFoldingSet<EntryType, ASTContext &> &FS,
ToRemove.push_back(&N);
for (auto *N : ToRemove)
- FS.RemoveNode(N);
+ FS.erase(N);
}
/// Erase from DenseMap based on predicate
@@ -79,7 +102,7 @@ static void eraseDenseSetIf(llvm::DenseSet<ValueT> &Set, PredT &&Pred) {
Set.erase(Key);
}
-void ASTContextStateStash::stash(StashCheckPoint &CP) {
+void ASTContextStateRecovery::stash(StashCheckPoint &CP) {
CP.TypesSize = Ctx.Types.size();
CP.ExtQualNodesSize = Ctx.ExtQualNodes.size();
CP.ComplexTypesSize = Ctx.ComplexTypes.size();
@@ -188,7 +211,8 @@ void ASTContextStateStash::stash(StashCheckPoint &CP) {
CP.GlobalArrayOperatorDeletesForVirtualDtorSize =
Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size();
- CP.RequireVectorDeletingDtorSize = Ctx.RequireVectorDeletingDtor.size();
+ CP.MaybeRequireVectorDeletingDtorSize =
+ Ctx.MaybeRequireVectorDeletingDtor.size();
CP.MergedDeclsSize = Ctx.MergedDecls.size();
CP.DeclAttrsSize = Ctx.DeclAttrs.size();
@@ -200,7 +224,7 @@ void ASTContextStateStash::stash(StashCheckPoint &CP) {
CP.ScalableVecTyMapSize = Ctx.ScalableVecTyMap.size();
CP.LambdaCastPathsSize = Ctx.LambdaCastPaths.size();
- CP.DeclRawCommentsSize = Ctx.DeclRawComments.size();
+ CP.RawCommentsSize = Ctx.RawComments.size();
CP.RedeclChainCommentsSize = Ctx.RedeclChainComments.size();
CP.CommentlessRedeclChainsSize = Ctx.CommentlessRedeclChains.size();
CP.ParsedCommentsSize = Ctx.ParsedComments.size();
@@ -236,11 +260,209 @@ void ASTContextStateStash::stash(StashCheckPoint &CP) {
CP.ObjCClassDeclCP = Ctx.ObjCClassDecl;
- Ctx.ObjCProtocolClassDecl = CP.ObjCProtocolClassDeclCP;
+ CP.ObjCProtocolClassDeclCP = Ctx.ObjCProtocolClassDecl;
+
+ /// DeclarationNameTable
+ CP.DeclarationNames.CXXConstructorNamesSize =
+ Ctx.DeclarationNames.CXXConstructorNames.size();
+ CP.DeclarationNames.CXXDestructorNamesSize =
+ Ctx.DeclarationNames.CXXDestructorNames.size();
+ CP.DeclarationNames.CXXConversionFunctionNamesSize =
+ Ctx.DeclarationNames.CXXConversionFunctionNames.size();
+ for (unsigned I = 0; I < NUM_OVERLOADED_OPERATORS; I++)
+ CP.DeclarationNames.CXXOperatorNamesCP[I] =
+ Ctx.DeclarationNames.CXXOperatorNames[I];
+ CP.DeclarationNames.CXXLiteralOperatorNamesSize =
+ Ctx.DeclarationNames.CXXLiteralOperatorNames.size();
+ CP.DeclarationNames.CXXDeductionGuideNamesSize =
+ Ctx.DeclarationNames.CXXDeductionGuideNames.size();
}
-void ASTContextStateStash::restore(StashCheckPoint &CP,
- llvm::SlabCheckPoint SlabCP) {
+class DeclContextRepairer {
+ ASTContext &Ctx;
+ llvm::SlabCheckPoint SlabCP;
+ // in case to avoid revisit.
+ llvm::SmallPtrSet<const DeclContext *, 8> Visited;
+
+ bool isAfterCP(const void *P) const {
+ return Ctx.getAllocator().isAfterCheckpoint(P, SlabCP);
+ }
+
+ static bool isExtensibleContainer(const Decl *D) {
+ return isa<NamespaceDecl>(D) || isa<TagDecl>(D);
+ }
+
+ static bool isRedeclarableOrOnlyDecl(Decl *D) {
+ return D->getPreviousDecl() != nullptr;
+ }
+
+ // Walk D's redecl chain looking for the newest decl that was from previous
+ // PTU. Returns nullptr if the entire chain was created this PTU.
+ template <typename DeclT> DeclT *findSurvivor(DeclT *D) {
+ for (DeclT *It = D->getMostRecentDecl(); It; It = It->getPreviousDecl()) {
+ if (!isAfterCP(It))
+ return It;
+ if (It == It->getFirstDecl())
+ break;
+ }
+ return nullptr;
+ }
+
+ template <typename decl_type>
+ void patchRedeclLink(Redeclarable<decl_type> *D, decl_type *Survivor) {
+ D->getFirstDecl()->RedeclLink.setLatest(Survivor);
+ }
+
+ void repairLexicalChain(DeclContext &DC) {
+ Decl *Prev = nullptr;
+ for (Decl *Cur = DC.FirstDecl; Cur; Cur = Cur->getNextDeclInContext()) {
+ if (isAfterCP(Cur)) {
+ if (Prev) {
+ DC.LastDecl = Prev;
+ Prev->NextInContextAndBits.setPointer(nullptr);
+ } else {
+ DC.FirstDecl = DC.LastDecl = nullptr; // nothing survives
+ }
+ return;
+ }
+ Prev = Cur;
+ }
+ }
+
+ NamedDecl *tryRepairRedeclChain(Decl *D) {
+ if (!isRedeclarableOrOnlyDecl(D))
+ return nullptr; // nothing to do
+
+ NamedDecl *Survivor = nullptr;
+
+ auto repairRedeclChain = [&](auto *RD) {
+ // using DeclT = std::remove_pointer_t<decltype(RD)>;
+
+ auto *SurvivorDecl = findSurvivor(RD);
+ if (SurvivorDecl)
+ patchRedeclLink(RD, SurvivorDecl);
+
+ Survivor = SurvivorDecl;
+ };
+
+ switch (D->getKind()) {
+ case Decl::Function:
+ repairRedeclChain(cast<FunctionDecl>(D));
+ break;
+
+ case Decl::Var:
+ repairRedeclChain(cast<VarDecl>(D));
+ break;
+
+ case Decl::Enum:
+ repairRedeclChain(cast<TagDecl>(D));
+ break;
+
+ case Decl::Record:
+ case Decl::CXXRecord:
+ repairRedeclChain(cast<TagDecl>(D));
+ break;
+
+ case Decl::ClassTemplate:
+ repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
+ break;
+
+ case Decl::FunctionTemplate:
+ repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
+ break;
+
+ case Decl::TypeAliasTemplate:
+ repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
+ break;
+
+ case Decl::VarTemplate:
+ repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
+ break;
+
+ case Decl::Namespace:
+ repairRedeclChain(cast<NamespaceDecl>(D));
+ break;
+
+ default:
+ break;
+ }
+
+ return Survivor;
+ }
+
+ void repairLookupEntry(StoredDeclsMap &Map, DeclarationName Key,
+ StoredDeclsList &List, bool Recurse) {
+ // Snapshot first: remove()/addOrReplaceDecl() below invalidate the live
+ // view getLookupResult() returns.
+ SmallVector<NamedDecl *, 4> Snapshot(List.getLookupResult().begin(),
+ List.getLookupResult().end());
+
+ for (NamedDecl *D : Snapshot) {
+ if (Recurse && isExtensibleContainer(D))
+ repairDeclContextLookup(cast<DeclContext>(D), Recurse);
+
+ if (!isAfterCP(D))
+ continue; // predates this PTU -- untouched
+
+ NamedDecl *Survivor = tryRepairRedeclChain(D); // may be null
+
+ if (!Survivor) {
+ List.remove(D); // whole chain born this PTU
+ continue;
+ }
+ assert(Survivor != D && "survivor postdates the checkpoint");
+
+ // If the survivor is already visible under this name, D was an extra
+ // entry (an overload); otherwise D had REPLACED the survivor's slot
+ // via HandleRedeclaration and has to hand it back.
+ if (llvm::is_contained(Snapshot, Survivor))
+ List.remove(D);
+ else
+ List.addOrReplaceDecl(Survivor);
+ }
+
+ if (List.isNull())
+ Map.erase(Key);
+ }
+
+public:
+ DeclContextRepairer(ASTContext &Ctx, llvm::SlabCheckPoint CP)
+ : Ctx(Ctx), SlabCP(CP) {}
+
+ template <typename decl_type>
+ void repairRedeclchain(Redeclarable<decl_type> *D) {
+ decl_type *SurviourDecl = findSurvivor(D);
+ if (SurviourDecl)
+ patchRedeclLink(D, SurviourDecl);
+ }
+
+ // Public entry point -- recursivly repair affected DCs.
+ void repairDeclContextLookup(DeclContext *DC, bool NoRecursive = false) {
+ DeclContext *Primary = DC->getPrimaryContext();
+ if (!Visited.insert(Primary).second)
+ return;
+
+ bool CompletelyNew = isAfterCP(cast<Decl>(Primary)->getCanonicalDecl());
+ StoredDeclsMap *Map = Primary->getLookupPtr();
+
+ if (CompletelyNew) {
+ // Primary's very first declaration happened this PTU -- the entire
+ // entity is being discarded as a unit, so there's nothing to
+ // individually repair.
+ if (Map)
+ Map->clear();
+ return;
+ }
+
+ if (Map)
+ for (auto &Entry : *Map)
+ repairLookupEntry(*Map, Entry.first, Entry.second, NoRecursive);
+ repairLexicalChain(*Primary);
+ }
+};
+
+void ASTContextStateRecovery::restore(StashCheckPoint &CP,
+ llvm::SlabCheckPoint SlabCP) {
if (CP.AutoDeductTy != Ctx.AutoDeductTy) {
llvm::dbgs() << "Ctx.AutoDeductTy != CP.AutoDeductTy\n";
Ctx.AutoDeductTy = CP.AutoDeductTy;
@@ -678,8 +900,8 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
// mutable llvm::DenseMap<llvm::FoldingSetNodeID, AutoType *> AutoTypes;
eraseDenseMapIf(
Ctx.AutoTypes,
- [&](llvm::detail::DenseMapPair<llvm::FoldingSetNodeID, AutoType *> &KV)
- -> bool {
+ [&](llvm::detail::DenseMapPair<llvm::FoldingSetNodeIDRef, AutoType *>
+ &KV) -> bool {
return Ctx.getAllocator().isAfterCheckpoint(
static_cast<void *>(KV.getSecond()), SlabCP);
});
@@ -1110,17 +1332,18 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size());
}
- if (CP.RequireVectorDeletingDtorSize !=
- Ctx.RequireVectorDeletingDtor.size()) {
- llvm::dbgs() << "if (CP.RequireVectorDeletingDtorSize != "
- "Ctx.RequireVectorDeletingDtor.size()\n";
- eraseDenseSetIf(
- Ctx.RequireVectorDeletingDtor, [&](const CXXRecordDecl *RD) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(const_cast<CXXRecordDecl *>(RD)), SlabCP);
- });
- assert(CP.RequireVectorDeletingDtorSize ==
- Ctx.RequireVectorDeletingDtor.size());
+ if (CP.MaybeRequireVectorDeletingDtorSize !=
+ Ctx.MaybeRequireVectorDeletingDtor.size()) {
+ llvm::dbgs() << "if (CP.MaybeRequireVectorDeletingDtorSize != "
+ "Ctx.MaybeRequireVectorDeletingDtor.size()\n";
+ eraseDenseSetIf(Ctx.MaybeRequireVectorDeletingDtor,
+ [&](const CXXRecordDecl *RD) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(
+ static_cast<void *>(const_cast<CXXRecordDecl *>(RD)),
+ SlabCP);
+ });
+ assert(CP.MaybeRequireVectorDeletingDtorSize ==
+ Ctx.MaybeRequireVectorDeletingDtor.size());
}
if (CP.MergedDeclsSize != Ctx.MergedDecls.size()) {
@@ -1176,9 +1399,9 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
assert(CP.LambdaCastPathsSize == Ctx.LambdaCastPaths.size());
}
- if (CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()) {
- llvm::dbgs() << "CP.DeclRawCommentsSize != Ctx.DeclRawComments.size()\n";
- assert(CP.DeclRawCommentsSize == Ctx.DeclRawComments.size());
+ if (CP.RawCommentsSize != Ctx.RawComments.size()) {
+ llvm::dbgs() << "CP.RawCommentsSize != Ctx.RawComments.size()\n";
+ assert(CP.RawCommentsSize == Ctx.RawComments.size());
}
if (CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()) {
@@ -1224,6 +1447,23 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
if (CP.TemplateOrInstantiationSize != Ctx.TemplateOrInstantiation.size()) {
llvm::dbgs() << "CP.TemplateOrInstantiationSize != "
"Ctx.TemplateOrInstantiation.size()\n";
+ // llvm::DenseMap<const VarDecl *,
+ // TemplateOrSpecializationInfo>
+ // TemplateOrInstantiation;
+ eraseDenseMapIf(
+ Ctx.TemplateOrInstantiation,
+ [&](llvm::detail::DenseMapPair<
+ const VarDecl *, ASTContext::TemplateOrSpecializationInfo> &KV)
+ -> bool {
+ ASTContext::TemplateOrSpecializationInfo &T = KV.getSecond();
+ bool IsAfterCP = false;
+ if (auto *M = T.dyn_cast<MemberSpecializationInfo *>())
+ IsAfterCP = Ctx.getAllocator().isAfterCheckpoint(M, SlabCP);
+ if (auto *V = T.dyn_cast<VarTemplateDecl *>())
+ IsAfterCP = Ctx.getAllocator().isAfterCheckpoint(V, SlabCP);
+ return IsAfterCP ||
+ Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
+ });
assert(CP.TemplateOrInstantiationSize ==
Ctx.TemplateOrInstantiation.size());
}
@@ -1339,7 +1579,6 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
assert(CP.TraversalScopeSize == Ctx.TraversalScope.size());
}
-
/// Obj-C
if (CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()) {
llvm::dbgs() << "CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()\n";
@@ -1383,55 +1622,120 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
assert(CP.ObjCProtocolClassDeclCP == Ctx.ObjCProtocolClassDecl);
}
- for (auto &[Decl, OldTy] : Ctx.PendingTypeForDeclMutations)
- Decl->TypeForDecl = OldTy;
-
- Ctx.PendingTypeForDeclMutations.clear();
- TranslationUnitDecl *MostRecentTU = Ctx.getTranslationUnitDecl();
- for (const auto *DC : Ctx.PendingDCMutations) {
- if (MostRecentTU->getPrimaryContext() == DC)
- continue;
- if (StoredDeclsMap *Map = const_cast<DeclContext *>(DC)
- ->getPrimaryContext()
- ->getLookupPtr()) {
- for (auto &&[Key, List] : *Map) {
- DeclContextLookupResult R = List.getLookupResult();
- std::vector<NamedDecl *> NamedDeclsToRemove;
- // bool RemoveAll = true;
- for (NamedDecl *D : R) {
- // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU (" <<
- // (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
- // llvm::outs() << "DeclContext : " << DC << "\n";
- // D->dump();
- // if (D->getTranslationUnitDecl() == MostRecentTU)
- if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D),
- SlabCP))
- NamedDeclsToRemove.push_back(D);
- // else
- // RemoveAll = false;
- }
- // if (LLVM_LIKELY(RemoveAll)) {
- // Map->erase(Key);
- // } else {
- for (NamedDecl *D : NamedDeclsToRemove)
- List.remove(D);
- // }
- }
- }
-
- // Decl *Prev = DC->FirstDecl;
- // Decl *Cur = DC->FirstDecl;
- // while (Cur) {
- // if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
- // SlabCP)) {
- // DC->LastDecl = Prev;
- // DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
- // break;
- // }
- // Prev = Cur;
- // Cur = Cur->getNextDeclInContext();
- // }
- }
+ if (CP.DeclarationNames.CXXConstructorNamesSize !=
+ Ctx.DeclarationNames.CXXConstructorNames.size()) {
+ llvm::dbgs() << "CP.DeclarationNames.CXXConstructorNamesSize != "
+ "Ctx.DeclarationNames.CXXConstructorNames.size()";
+ eraseFoldingSetIf(Ctx.DeclarationNames.CXXConstructorNames,
+ [&](detail::CXXSpecialNameExtra &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(&Node,
+ SlabCP);
+ });
+ assert(CP.DeclarationNames.CXXConstructorNamesSize ==
+ Ctx.DeclarationNames.CXXConstructorNames.size());
+ }
+ if (CP.DeclarationNames.CXXDestructorNamesSize !=
+ Ctx.DeclarationNames.CXXDestructorNames.size()) {
+ llvm::dbgs() << "CP.DeclarationNames.CXXDestructorNamesSize != "
+ "Ctx.DeclarationNames.CXXDestructorNames.size()";
+ eraseFoldingSetIf(Ctx.DeclarationNames.CXXDestructorNames,
+ [&](detail::CXXSpecialNameExtra &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(&Node,
+ SlabCP);
+ });
+ assert(CP.DeclarationNames.CXXDestructorNamesSize ==
+ Ctx.DeclarationNames.CXXDestructorNames.size());
+ }
+ if (CP.DeclarationNames.CXXConversionFunctionNamesSize !=
+ Ctx.DeclarationNames.CXXConversionFunctionNames.size()) {
+ llvm::dbgs() << "CP.DeclarationNames.CXXConversionFunctionNamesSize != "
+ "Ctx.DeclarationNames.CXXConversionFunctionNames.size()";
+ eraseFoldingSetIf(Ctx.DeclarationNames.CXXConversionFunctionNames,
+ [&](detail::CXXSpecialNameExtra &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(&Node,
+ SlabCP);
+ });
+ assert(CP.DeclarationNames.CXXConversionFunctionNamesSize ==
+ Ctx.DeclarationNames.CXXConversionFunctionNames.size());
+ }
+ if (CP.DeclarationNames.CXXLiteralOperatorNamesSize !=
+ Ctx.DeclarationNames.CXXLiteralOperatorNames.size()) {
+ llvm::dbgs() << "CP.DeclarationNames.CXXLiteralOperatorNamesSize != "
+ "Ctx.DeclarationNames.CXXLiteralOperatorNames.size()";
+ eraseFoldingSetIf(Ctx.DeclarationNames.CXXLiteralOperatorNames,
+ [&](detail::CXXLiteralOperatorIdName &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(&Node,
+ SlabCP);
+ });
+ assert(CP.DeclarationNames.CXXLiteralOperatorNamesSize ==
+ Ctx.DeclarationNames.CXXLiteralOperatorNames.size());
+ }
+ if (CP.DeclarationNames.CXXDeductionGuideNamesSize !=
+ Ctx.DeclarationNames.CXXDeductionGuideNames.size()) {
+ llvm::dbgs() << "CP.DeclarationNames.CXXDeductionGuideNamesSize != "
+ "Ctx.DeclarationNames.CXXDeductionGuideNames.size()";
+ eraseFoldingSetIf(Ctx.DeclarationNames.CXXDeductionGuideNames,
+ [&](detail::CXXDeductionGuideNameExtra &Node) -> bool {
+ return Ctx.getAllocator().isAfterCheckpoint(&Node,
+ SlabCP);
+ });
+ assert(CP.DeclarationNames.CXXDeductionGuideNamesSize ==
+ Ctx.DeclarationNames.CXXDeductionGuideNames.size());
+ }
+ for (unsigned I = 0; I < NUM_OVERLOADED_OPERATORS; I++)
+ Ctx.DeclarationNames.CXXOperatorNames[I] =
+ CP.DeclarationNames.CXXOperatorNamesCP[I];
+
+ // for (auto &[Decl, OldTy] : Ctx.PendingTypeForDeclMutations)
+ // Decl->TypeForDecl = OldTy;
+
+ // Ctx.PendingTypeForDeclMutations.clear();
+ // TranslationUnitDecl *MostRecentTU = Ctx.getTranslationUnitDecl();
+ // for (const auto *DC : Ctx.PendingDCMutations) {
+ // // if (MostRecentTU->getPrimaryContext() == DC)
+ // // continue;
+ // if (StoredDeclsMap *Map = const_cast<DeclContext *>(DC)
+ // ->getPrimaryContext()
+ // ->getLookupPtr()) {
+ // for (auto &&[Key, List] : *Map) {
+ // DeclContextLookupResult R = List.getLookupResult();
+ // std::vector<NamedDecl *> NamedDeclsToRemove;
+ // // bool RemoveAll = true;
+ // for (NamedDecl *D : R) {
+ // // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU ("
+ // <<
+ // // (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
+ // // llvm::outs() << "DeclContext : " << DC << "\n";
+ // // D->dump();
+ // // if (D->getTranslationUnitDecl() == MostRecentTU)
+ // if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D),
+ // SlabCP))
+ // NamedDeclsToRemove.push_back(D);
+ // // else
+ // // RemoveAll = false;
+ // }
+ // // if (LLVM_LIKELY(RemoveAll)) {
+ // // Map->erase(Key);
+ // // } else {
+ // for (NamedDecl *D : NamedDeclsToRemove)
+ // List.remove(D);
+ // // }
+ // }
+ // }
+
+ // Decl *Prev = DC->FirstDecl;
+ // Decl *Cur = DC->FirstDecl;
+ // while (Cur) {
+ // if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
+ // SlabCP)) {
+ // DC->LastDecl = Prev;
+ // DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
+ // break;
+ // }
+ // Prev = Cur;
+ // Cur = Cur->getNextDeclInContext();
+ // }
+ // }
// if (FirstDecl) {
// LastDecl->NextInContextAndBits.setPointer(D);
@@ -1469,8 +1773,6 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
// /// end of the import declaration.
// llvm::PointerIntPair<ImportDecl *, 1, bool> NextLocalImportAndComplete;
- Ctx.PendingDCMutations.clear();
-
// llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM = Ctx.LastSDM;
// StoredDeclsMap *Map = LastSDM.getPointer();
@@ -1502,8 +1804,5 @@ void ASTContextStateStash::restore(StashCheckPoint &CP,
Ctx.Types.resize(CP.TypesSize);
}
-void ASTContextStateStash::commit() {
- Ctx.PendingTypeForDeclMutations.clear();
- Ctx.PendingDCMutations.clear();
-}
+void ASTContextStateRecovery::commit() {}
} // end namespace clang
\ No newline at end of file
diff --git a/clang/lib/Interpreter/IncrementalAction.cpp b/clang/lib/Interpreter/IncrementalAction.cpp
index 751cc7b58a034..2c1ad404a3e7d 100644
--- a/clang/lib/Interpreter/IncrementalAction.cpp
+++ b/clang/lib/Interpreter/IncrementalAction.cpp
@@ -54,7 +54,7 @@ IncrementalAction::IncrementalAction(CompilerInstance &Instance,
}
return Act;
}()),
- Interp(I), CI(Instance), LLVMCtx(LLVMCtx), Consumer(std::move(Consumer)) {}
+ Interp(I), CI(Instance), Consumer(std::move(Consumer)) {}
std::unique_ptr<ASTConsumer>
IncrementalAction::CreateASTConsumer(CompilerInstance & /*CI*/,
@@ -97,10 +97,8 @@ llvm::Module *IncrementalAction::getCachedCodeGenModule() const {
return CachedInCodeGenModule.get();
}
-std::unique_ptr<llvm::Module> IncrementalAction::GenModule(bool WasFailure) {
+std::unique_ptr<llvm::Module> IncrementalAction::GenModule() {
static unsigned ID = 0;
- if (WasFailure)
- --ID;
if (CodeGenerator *CG = getCodeGen()) {
// Clang's CodeGen is designed to work with a single llvm::Module. In many
// cases for convenience various CodeGen parts have a reference to the
@@ -117,10 +115,8 @@ std::unique_ptr<llvm::Module> IncrementalAction::GenModule(bool WasFailure) {
CachedInCodeGenModule->alias_empty() &&
CachedInCodeGenModule->ifunc_empty())) &&
"CodeGen wrote to a readonly module");
- std::unique_ptr<llvm::Module> M = nullptr;
- if (!WasFailure)
- M = CG->ReleaseModule();
- CG->StartModule("incr_module_" + std::to_string(ID++), M ? M->getContext() : LLVMCtx);
+ std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
+ CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
return M;
}
return nullptr;
diff --git a/clang/lib/Interpreter/IncrementalAction.h b/clang/lib/Interpreter/IncrementalAction.h
index 13461dee7500f..c3e4f108ea365 100644
--- a/clang/lib/Interpreter/IncrementalAction.h
+++ b/clang/lib/Interpreter/IncrementalAction.h
@@ -36,7 +36,7 @@ class IncrementalAction : public WrapperFrontendAction {
bool IsTerminating = false;
Interpreter &Interp;
[[maybe_unused]] CompilerInstance &CI;
- llvm::LLVMContext &LLVMCtx;
+ // llvm::LLVMContext &LLVMCtx;
std::unique_ptr<ASTConsumer> Consumer;
/// When CodeGen is created the first llvm::Module gets cached in many places
@@ -75,7 +75,7 @@ class IncrementalAction : public WrapperFrontendAction {
CodeGenerator *getCodeGen() const;
/// Generate an LLVM module for the most recent parsed input.
- std::unique_ptr<llvm::Module> GenModule(bool WasFailure = false);
+ std::unique_ptr<llvm::Module> GenModule();
};
class InProcessPrintingASTConsumer final : public MultiplexConsumer {
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index 6f913867ad159..ed8919d8af59e 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -17,8 +17,12 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclContextInternals.h"
+<<<<<<< HEAD
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclVisitor.h"
+=======
+#include "clang/CodeGen/ModuleBuilder.h"
+>>>>>>> 6d90b8675095 (Add Initial impl ErrorRecovery.h)
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/Parse/Parser.h"
@@ -88,9 +92,8 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
DiagnosticsEngine &Diags = S.getDiagnostics();
if (Diags.hasErrorOccurred()) {
- Consumer->HandleTranslationUnit(C);
CleanUpPTU(C.getTranslationUnitDecl());
-
+ // Consumer->HandleTranslationUnit(C);
Diags.Reset(/*soft=*/true);
Diags.getClient()->clear();
return llvm::make_error<llvm::StringError>("Parsing failed.",
@@ -330,8 +333,8 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
// Lookup alone is not enough: the redeclaration chain still reaches these.
withdrawMostRecentTU(MostRecentTU);
- RepairRedeclChain(MostRecentTU, MostRecentTU);
- S.getASTContext().setTranslationUnitDecl(MostRecentTU->getPreviousDecl());
+ // RepairRedeclChain(MostRecentTU, MostRecentTU);
+ // S.getASTContext().setTranslationUnitDecl(MostRecentTU->getPreviousDecl());
}
PartialTranslationUnit &
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 63961133b907d..bf9d20653727a 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -593,8 +593,8 @@ Interpreter::Parse(llvm::StringRef Code) {
private:
ASTContext &Ctx;
- ASTContextStateStash ASTCtxState;
- SemaStateStash SemaState;
+ ASTContextStateRecovery ASTCtxState;
+ SemaStateRecovery SemaState;
llvm::SlabCheckPoint CheckPoint;
bool Committed = false;
StashCheckPoint CtxCheckPoint;
@@ -624,11 +624,11 @@ Interpreter::Parse(llvm::StringRef Code) {
getCompilerInstance()->getDiagnostics().setSeverity(
clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation());
- PTUSlabRollback Rollback(CI->getSema());
+ // PTUSlabRollback Rollback(CI->getSema());
llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
if (!TuOrErr) {
- Act->GenModule(true);
+ // Act->GenModule();
return TuOrErr.takeError();
}
@@ -640,7 +640,7 @@ Interpreter::Parse(llvm::StringRef Code) {
frontend::EmitLLVM)
LastPTU.TheModule->print(llvm::outs(), /*AAW=*/nullptr);
- Rollback.commit(LastPTU);
+ // Rollback.commit(LastPTU);
return LastPTU;
}
diff --git a/clang/lib/Interpreter/SemaStateStash.cpp b/clang/lib/Interpreter/SemaStateStash.cpp
index e72d0b7580bcb..1c24b634b9443 100644
--- a/clang/lib/Interpreter/SemaStateStash.cpp
+++ b/clang/lib/Interpreter/SemaStateStash.cpp
@@ -1,4 +1,4 @@
-//===--- SemaStateStash.cpp - Sema persistent state stash/restore
+//===--- SemaStateRecovery.cpp - Sema persistent state stash/restore
//----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -29,8 +29,7 @@
namespace clang {
template <typename EntryType, typename PredT>
-static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred)
-{
+static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
SmallVector<EntryType *, 16> ToRemove;
for (auto &N : FS)
if (Pred(N))
@@ -116,7 +115,7 @@ static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, SmallSize> &Set,
// Pragma / value snapshot (uses Sema friend access for nested types)
//===----------------------------------------------------------------------===//
-// struct SemaStateStash::PragmaSnapshot {
+// struct SemaStateRecovery::PragmaSnapshot {
// Sema::PragmaClangSection PragmaClangBSSSection;
// Sema::PragmaClangSection PragmaClangDataSection;
// Sema::PragmaClangSection PragmaClangRodataSection;
@@ -140,7 +139,7 @@ static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, SmallSize> &Set,
// FileNullabilityMap NullabilityMap;
// };
-void SemaStateStash::stash(SemaStashCheckPoint &CP) {
+void SemaStateRecovery::stash(SemaStashCheckPoint &CP) {
CP.SemaBumpSlabCP = S.BumpAlloc.checkPoint();
// CP.CachedFunctionScopeSize = S.CachedFunctionScope.size();
CP.FunctionScopesSize = S.FunctionScopes.size();
@@ -230,7 +229,7 @@ void SemaStateStash::stash(SemaStashCheckPoint &CP) {
S.CodeSynthesisContextLookupModules.size();
CP.LookupModulesCacheSize = S.LookupModulesCache.size();
CP.VisibleNamespaceCacheSize = S.VisibleNamespaceCache.size();
- CP.TemplateInstCallbacksSize = S.TemplateInstCallbacks.size();
+ // CP.TemplateInstCallbacksSize = S.TemplateInstCallbacks.size();
CP.PendingInstantiationsSize = S.PendingInstantiations.size();
CP.LateParsedInstantiationsSize = S.LateParsedInstantiations.size();
CP.SavedVTableUsesSize = S.SavedVTableUses.size();
@@ -239,6 +238,8 @@ void SemaStateStash::stash(SemaStashCheckPoint &CP) {
S.PendingLocalImplicitInstantiations.size();
CP.UnsubstitutedConstraintSatisfactionCacheSize =
S.UnsubstitutedConstraintSatisfactionCache.size();
+ if (S.CurrentCachedTemplateArgs)
+ CP.CurrentCachedTemplateArgsSize = S.CurrentCachedTemplateArgs->size();
CP.SubsumptionCacheSize = S.SubsumptionCache.size();
CP.NormalizationCacheSize = S.NormalizationCache.size();
CP.SatisfactionCacheSize = S.SatisfactionCache.size();
@@ -248,8 +249,8 @@ void SemaStateStash::stash(SemaStashCheckPoint &CP) {
// CP.AllEffectsToVerifySize = S.AllEffectsToVerify.size();
}
-void SemaStateStash::restore(SemaStashCheckPoint &CP,
- llvm::SlabCheckPoint SlabCP) {
+void SemaStateRecovery::restore(SemaStashCheckPoint &CP,
+ llvm::SlabCheckPoint SlabCP) {
ASTContext &Ctx = S.getASTContext();
if (CP.FunctionScopesSize != S.FunctionScopes.size()) {
@@ -438,7 +439,8 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
*S.PureVirtualClassDiagSet.get(), [&](const CXXRecordDecl *RD) -> bool {
return Ctx.getAllocator().isAfterCheckpoint(RD, SlabCP);
});
- // assert(CP.PureVirtualClassDiagSetSize == S.PureVirtualClassDiagSet->size());
+ // assert(CP.PureVirtualClassDiagSetSize ==
+ // S.PureVirtualClassDiagSet->size());
}
if (CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.end()) {
@@ -464,7 +466,7 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
if (CP.UndefinedButUsedSize != S.UndefinedButUsed.size()) {
llvm::dbgs() << "CP.UndefinedButUsedSize != S.UndefinedButUsed.size()\n";
- // llvm::MapVector<NamedDecl *, SourceLocation> UndefinedButUsed;
+ // llvm::MapVector<NamedDecl *, SourceLocation> UndefinedButUsed;
while (CP.UndefinedButUsedSize != S.UndefinedButUsed.size())
S.UndefinedButUsed.pop_back();
assert(CP.UndefinedButUsedSize == S.UndefinedButUsed.size());
@@ -580,11 +582,13 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
"S.TypoCorrectionFailures.size()\n";
// eraseDenseMapIf(
// S.TypoCorrectionFailures,
- // [&](llvm::detail::DenseMapPair<IdentifierInfo *, Sema::SrcLocSet> &KV)
+ // [&](llvm::detail::DenseMapPair<IdentifierInfo *, Sema::SrcLocSet>
+ // &KV)
// -> bool {
- // llvm::outs() << "SlabCheckPoint Cur : " << (void *)SlabCP.CurPtr << "\n";
- // llvm::outs() << "SlabCheckPoint END : " << (void *)SlabCP.End << "\n";
- // llvm::outs() << "Addr: " << static_cast<void *>(KV.getFirst()) << "\n";
+ // llvm::outs() << "SlabCheckPoint Cur : " << (void
+ // *)SlabCP.CurPtr << "\n"; llvm::outs() << "SlabCheckPoint END :
+ // " << (void *)SlabCP.End << "\n"; llvm::outs() << "Addr: " <<
+ // static_cast<void *>(KV.getFirst()) << "\n";
// return Ctx.getAllocator().isAfterCheckpoint(
// static_cast<void *>(KV.getFirst()), SlabCP);
// });
@@ -737,13 +741,13 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
assert(CP.VisibleNamespaceCacheSize == S.VisibleNamespaceCache.size());
}
- if (CP.TemplateInstCallbacksSize != S.TemplateInstCallbacks.size()) {
- llvm::dbgs() << "CP.TemplateInstCallbacksSize != "
- "S.TemplateInstCallbacks.size()\n";
- // std::vector<std::unique_ptr<TemplateInstantiationCallback>>
- // TemplateInstCallbacks;
- assert(CP.TemplateInstCallbacksSize == S.TemplateInstCallbacks.size());
- }
+ // if (CP.TemplateInstCallbacksSize != S.TemplateInstCallbacks.size()) {
+ // llvm::dbgs() << "CP.TemplateInstCallbacksSize != "
+ // "S.TemplateInstCallbacks.size()\n";
+ // // std::vector<std::unique_ptr<TemplateInstantiationCallback>>
+ // // TemplateInstCallbacks;
+ // assert(CP.TemplateInstCallbacksSize == S.TemplateInstCallbacks.size());
+ // }
if (CP.PendingInstantiationsSize != S.PendingInstantiations.size()) {
llvm::dbgs() << "CP.PendingInstantiationsSize != "
@@ -787,6 +791,22 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
S.PendingLocalImplicitInstantiations.size());
}
+ if (S.CurrentCachedTemplateArgs && (CP.CurrentCachedTemplateArgsSize !=
+ S.CurrentCachedTemplateArgs->size())) {
+ llvm::dbgs() << "CP.CurrentCachedTemplateArgsSize != "
+ "S.CurrentCachedTemplateArgs->size()\n ";
+ // eraseDenseMapIf(
+ // S.UnsubstitutedConstraintSatisfactionCache,
+ // [&](UnsubstitutedConstraintSatisfactionCacheResult
+ // &R) -> bool {
+ // return Ctx.isAfterCheckpoint(static_cast<void
+ // *>(R.SubstExpr.get()),
+ // SlabCP)
+ // });
+ assert(CP.CurrentCachedTemplateArgsSize ==
+ S.CurrentCachedTemplateArgs->size());
+ }
+
if (CP.UnsubstitutedConstraintSatisfactionCacheSize !=
S.UnsubstitutedConstraintSatisfactionCache.size()) {
llvm::dbgs() << "CP.UnsubstitutedConstraintSatisfactionCacheSize != "
@@ -859,12 +879,15 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
if (CP.SpecialMemberCacheSize != S.SpecialMemberCache.size()) {
llvm::dbgs() << "CP.SpecialMemberCacheSize != "
"S.SpecialMemberCache.size()\n";
-
- eraseFoldingSetIf(
+
+ eraseDenseMapIf(
S.SpecialMemberCache,
- [&](Sema::SpecialMemberOverloadResultEntry &Node) -> bool {
- return S.BumpAlloc.isAfterCheckpoint(static_cast<void *>(&Node),
- CP.SemaBumpSlabCP);
+ [&](llvm::detail::DenseMapPair<Sema::SpecialMemberCacheKey,
+ Sema::SpecialMemberOverloadResult> &KV)
+ -> bool {
+ return S.BumpAlloc.isAfterCheckpoint(
+ static_cast<void *>(KV.getSecond().getMethod()),
+ CP.SemaBumpSlabCP);
});
assert(CP.SpecialMemberCacheSize == S.SpecialMemberCache.size());
}
@@ -876,7 +899,8 @@ void SemaStateStash::restore(SemaStashCheckPoint &CP,
assert(isa<NamedDecl>(TmpD) && "Decl isn't NamedDecl?");
NamedDecl *D = cast<NamedDecl>(TmpD);
- if (!D->getDeclName()) continue;
+ if (!D->getDeclName())
+ continue;
if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D), SlabCP)) {
if (D->getDeclName().getFETokenInfo())
diff --git a/clang/test/Interpreter/ptu-rewind-stress.cpp b/clang/test/Interpreter/ptu-rewind-stress.cpp
index f38b1c5fb9731..48f3f0835af31 100644
--- a/clang/test/Interpreter/ptu-rewind-stress.cpp
+++ b/clang/test/Interpreter/ptu-rewind-stress.cpp
@@ -4,137 +4,21 @@
// incremental input, the same way fail.cpp / code-undo.cpp do):
// RUN: cat %s | clang-repl | FileCheck %s
//
-// Usage interactively: paste blocks (or the whole file) directly at the
-// `clang-repl>` prompt. Each block is a single line on purpose -- clang-repl
-// treats one physical line as one Interpreter::Parse() call, and several of
-// these tests rely on packing a well-formed declaration and an ill-formed
-// one onto the *same* line so both go through Sema together before the
-// chunk is discarded as a whole.
-//
-// -----------------------------------------------------------------------
-// Observed results (assertions-enabled build, this checkout, macOS/arm64)
-// -----------------------------------------------------------------------
-// Running this file as-is through `./bin/clang-repl < ptu-rewind-stress.cpp`
-// does NOT make it to the %quit at the bottom:
-// * Test 1 (dangling_name) printed "dangling_name = 7" instead of 99, and
-// the JIT reported
-// error: In incr_module_89, duplicate definition of symbol '_dangling_name'
-// i.e. the rolled-back chunk's *codegen* (which runs incrementally per
-// top-level decl, before the end-of-chunk error check) was never undone
-// either, so the second, successful declaration silently reused the
-// first (supposedly-discarded) global.
-// * Test 3 (Boom<int> template instantiation) reliably aborts the process:
-// Assertion failed: (CP.UndefinedButUsedSize == S.UndefinedButUsed.size()),
-// function restore, file SemaStateStash.cpp, line 457.
-// i.e. real, confirmed, unconditional test-3-and-later block; a debug/
-// assertions build never reaches tests 4-7 in one session because of
-// this abort.
-// * Running tests 4-7 in isolation (skipping 1-3), Test 4 (sizeof on a
-// pre-existing type) produced
-// JIT session error: Symbols not found: [ _sz_known2 ]
-// error: Failed to materialize symbols: ...
-// followed by the process spinning at 100% CPU instead of returning to
-// the prompt for the remaining input (tests 5-7 never ran in that
-// session either).
-// * Running Test 1 completely on its own (no preceding chunks) did NOT
-// reproduce the wrong-value/duplicate-symbol failure -- it printed the
-// correct "dangling_name = 99". This is expected for a poisoned/reused-
-// memory bug: whether a stale pointer's target has been overwritten by
-// something that "looks wrong" depends on what the allocator handed out
-// to *other* code in between, so the manifestation is sensitive to the
-// exact preceding session history, not just to the isolated snippet.
-// That is precisely why this file chains many small scenarios in one
-// session rather than shipping them as independent one-liners.
-//
-// -----------------------------------------------------------------------
-// Background
-// -----------------------------------------------------------------------
-// Interpreter::Parse (clang/lib/Interpreter/Interpreter.cpp) wraps every
-// incremental input in a PTUSlabRollback guard:
-// 1. Takes CheckPoint = Ctx.getAllocator().checkPoint()
-// 2. Stashes ASTContext/Sema side-table sizes (ASTContextStateStash,
-// SemaStateStash)
-// 3. Runs IncrementalParser::Parse()
-// 4. On success: commits (keeps everything)
-// 5. On failure: SemaState.restore(), ASTCtxState.restore(), then
-// Ctx.getAllocator().restoreToCheckPoint(CheckPoint)
-//
-// restoreToCheckPoint (llvm/include/llvm/Support/Allocator.h) does not just
-// move a pointer back -- it actively memset()s the reclaimed range to 0xCD
-// ("poisonMemory") outside of ASan builds. So *any* pointer left behind in a
-// side-table that the two StateStash::restore() functions fail to clean up
-// is not merely suspect, it is guaranteed to point at either poisoned bytes
-// or, once new allocations reuse that space, at a completely unrelated
-// object (type confusion).
-//
-// Reading ASTContextStateStash.cpp and SemaStateStash.cpp shows the
-// restore() paths fall into three buckets:
-// (a) real cleanup: erase from the container based on
-// isAfterCheckpoint(ptr, SlabCP) [handles most Type folding sets]
-// (b) resize()/pop-back style cleanup on trailing-append vectors
-// [FunctionScopes, LateParsedInstantiations, SavedVTableUses, ...]
-// (c) `assert(oldSize == newSize)` with NO actual erase -- compiled away
-// entirely under NDEBUG, so the container is left holding dangling
-// pointers with no diagnostic at all in a release build
-// [StringLiteralCache, KeyFunctions-adjacent maps when the early
-// return below fires, MergedDecls, TemplateInstCallbacks,
-// PendingInstantiations, LateParsedTemplateMap, VTableUses/
-// VTablesUsed, ...]
-// Additionally, ASTContextStateStash::restore() opens with:
-// if (CP.TypesSize == Ctx.Types.size()) return;
-// which skips *all* of the above (including the folding sets that do have
-// real cleanup logic) whenever the number of interned Type nodes happens to
-// be unchanged -- even though many other caches (record layouts, key
-// functions, ...) can grow without interning a new Type.
-//
-// Every test below is built to land in one of these gaps. None of the
-// "poison" code paths depend on undefined behavior sanitizers to observe --
-// under a debug (assertions-enabled) build several of them should abort on
-// the assert() itself; under a release build the same inputs should
-// eventually crash, print garbage, or misbehave once the poisoned/reused
-// memory is dereferenced.
-// -----------------------------------------------------------------------
extern "C" int printf(const char *, ...);
// =======================================================================
// Test 1: Sema::IdResolver keeps a dangling entry after a failed parse.
-//
-// SemaStateStash::restore() (clang/lib/Interpreter/SemaStateStash.cpp)
-// walks S.getCurScope()->decls() and calls Scope::RemoveDecl() for every
-// decl allocated after the checkpoint -- but the matching
-// `S.IdResolver.RemoveDecl(D)` call right above it is commented out. Name
-// lookup goes through IdResolver, not just Scope, so `dangling_name`'s
-// VarDecl stays "found" by lookup even though its storage is about to be
-// poisoned by the allocator rewind.
-//
-// `int dangling_name = 7;` fully binds (Scope + IdResolver + DeclContext
-// lookup map) before `intentional_error_type` is even parsed, because
-// IncrementalParser::ParseOrWrapTopLevelDecl only checks
-// Diags.hasErrorOccurred() *after* parsing every top-level decl in the
-// chunk. So both statements are discarded together, but only Scope forgets
-// about `dangling_name`.
// =======================================================================
int dangling_name = 7; intentional_error_type garbage_after_dangling_name;
-// Redeclare the same identifier in a fresh, successful chunk. If the stale
-// IdResolver entry survived, Sema's redeclaration-merging logic
-// (Sema::MergeVarDecl* et al.) may try to compare this new VarDecl against
-// the dangling one -- reading 0xCD-poisoned memory, or memory since reused
-// by an unrelated allocation.
int dangling_name = 99;
auto t1 = printf("dangling_name = %d\n", dangling_name);
// CHECK: dangling_name = 99
// =======================================================================
// Test 2: Amplify test 1 by repeating the fail/redeclare cycle several
-// times on the same identifier. Because every failed chunk takes its
-// checkpoint at (almost) the same allocator offset, each failed attempt's
-// storage for `stress_var` gets reallocated over the *same* address range
-// poisoned by the previous attempt. Any stale IdResolver node left behind
-// by an earlier iteration therefore ends up aliasing whatever the *next*
-// iteration (or the final, successful declaration) allocates there --
-// classic type-confusion setup, and a good candidate to run under ASan.
+// times on the same identifier.
// =======================================================================
int stress_var = 0; intentional_error_type e0;
int stress_var = 1; intentional_error_type e1;
@@ -147,18 +31,6 @@ auto t2 = printf("stress_var = %d\n", stress_var);
// =======================================================================
// Test 3: Sema::PendingInstantiations does not survive a failed parse.
-//
-// ParseOrWrapTopLevelDecl() returns *before* calling
-// LocalInstantiations.perform()/GlobalInstantiations.perform() whenever
-// Diags.hasErrorOccurred() -- see the early `return` right after
-// CleanUpPTU() in IncrementalParser.cpp. So any instantiation work queued
-// while parsing `Boom<int> boom_instance; boom_instance.trigger();` is never
-// drained on this path. SemaStateStash::restore() only compares
-// S.PendingInstantiations.size() with an assert (it's a std::deque, never
-// resized/erased), so a stale PendingImplicitInstantiation entry pointing
-// at the (about-to-be-poisoned) `Boom<int>::trigger` specialization can
-// leak into whatever the *next* successful chunk's own eager-instantiation
-// pass processes.
// =======================================================================
template <typename T> struct Boom { void trigger() { T v; (void)v; } };
Boom<int> boom_instance; boom_instance.trigger(); intentional_error_type boom_garbage;
@@ -174,19 +46,6 @@ auto t3 = printf("after_boom_marker = %d\n", after_boom_marker);
// =======================================================================
// Test 4: ASTContextStateStash's Types.size() early-return guard skips
// cleanup of caches that don't depend on interning a new Type.
-//
-// void ASTContextStateStash::restore(...) {
-// if (CP.TypesSize == Ctx.Types.size())
-// return;
-// ... (all the real per-folding-set cleanup lives below this line) ...
-//
-// `AlreadyKnown` already exists (its RecordType was interned when it was
-// first declared, in the prior committed chunk). Computing sizeof() on it
-// only populates Ctx.ASTRecordLayouts (and, since it's a POD aggregate here,
-// nothing else) -- it does not intern a new Type, so Ctx.Types.size() is
-// unchanged across this failing chunk and the whole restore() body,
-// including ASTRecordLayouts's own (otherwise correct) erase-by-checkpoint
-// logic, is skipped.
// =======================================================================
struct AlreadyKnown { int a; int b; };
unsigned long sz_known = sizeof(AlreadyKnown); intentional_error_type sizeof_garbage;
@@ -201,10 +60,6 @@ auto t4 = printf("sizeof(AlreadyKnown) = %lu\n", sz_known2);
// Test 5: Virtual dispatch bookkeeping (Sema::VTableUses/VTablesUsed,
// ASTContext::KeyFunctions) is assert-only / guard-gated, same as above,
// but exercised through polymorphic classes and `new` instead of sizeof.
-// RecordLayoutBuilder.cpp populates Ctx.KeyFunctions[RD] while laying out
-// `Derived` for the `new` expression below; Sema::MarkVTableUsed populates
-// VTableUses/VTablesUsed. Both are torn down on a *committed* chunk but
-// only assert-compared on a rolled-back one.
// =======================================================================
struct Base { virtual int val() { return 1; } virtual ~Base() {} };
struct Derived : Base { int val() override { return 2; } };
@@ -219,35 +74,19 @@ auto t5 = printf("val = %d\n", bp2->val());
// CHECK: val = 2
// =======================================================================
-// Test 6: Ctx.StringLiteralCache has no erase logic at all -- not gated by
-// the Types.size() guard (defining a brand-new function interns a new
-// FunctionProtoType, so the early return above does *not* fire here), just
-// a bare `assert(CP.StringLiteralCacheSize == Ctx.StringLiteralCache.size())`
-// with nothing to actually undo the insertion made by
-// ASTContext::getPredefinedStringLiteralFromCache when Sema processes
-// __PRETTY_FUNCTION__. In an assertions-enabled build this is the test most
-// likely to abort immediately and deterministically, independent of memory
-// poisoning, purely because the size check itself fails.
+// Test 6: StringLiteralCache has no erase logic, so processing PRETTY_FUNCTION
+// leaves an extra entry and can make the cache-size assertion fail deterministically.
// =======================================================================
void uses_predefined_expr() { const char* pf = __PRETTY_FUNCTION__; (void)pf; } intentional_error_type predefined_garbage;
-// A second, differently-named function that also uses __PRETTY_FUNCTION__,
-// forcing another StringLiteralCache lookup/insert keyed differently from
-// the discarded one above.
void uses_predefined_expr_again() { const char* pf = __PRETTY_FUNCTION__; (void)pf; }
int pf_ran = 1;
auto t6 = printf("pf_ran = %d\n", pf_ran);
// CHECK: pf_ran = 1
// =======================================================================
-// Test 7: Cross-declaration merging state (Ctx.MergedDecls,
-// Ctx.InstantiatedFromUsingShadowDecl) survives a failed using-declaration.
-// A `using` declaration creates a UsingShadowDecl tied back to the
-// original NS::helper via a side map that SemaStateStash/
-// ASTContextStateStash only assert-compare. If the failed attempt's
-// UsingShadowDecl (and its bookkeeping entry) is left dangling, a
-// subsequent successful `using NS::helper;` shares the same DeclarationName
-// and could resolve through, or conflict with, the stale shadow chain.
+// Test 7: Cross-declaration merging state can survive a failed using-declaration,
+// leaving stale UsingShadowDecl bookkeeping that may affect a later successful using.
// =======================================================================
namespace NS { int helper() { return 10; } }
using NS::helper; intentional_error_type merge_garbage;
diff --git a/llvm/include/llvm/ADT/FoldingSet.h b/llvm/include/llvm/ADT/FoldingSet.h
index b611e015e84d7..75f654dac058e 100644
--- a/llvm/include/llvm/ADT/FoldingSet.h
+++ b/llvm/include/llvm/ADT/FoldingSet.h
@@ -620,6 +620,11 @@ template <class T, class VectorT = SmallVector<T *, 8>> class FoldingSetVector {
Vector.push_back(N);
}
+ void pop_back() {
+ Set.erase(Vector.back());
+ Vector.pop_back();
+ }
+
/// Insert the specified node into the folding set, knowing that
/// it is not already in the folding set.
void insert(T *N) {
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index 3709972ea99d8..0aceaa999e2a6 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -45,8 +45,7 @@ LLVM_ABI void printBumpPtrAllocatorStats(unsigned NumSlabs, size_t TotalMemory);
struct SlabCheckPoint {
unsigned ActiveSlabIdx;
char *CurPtr;
- char *End;
- size_t BytesAllocated;
+ uintptr_t EndSentinel;
};
/// Allocate memory in an ever growing pool, as if by bump-pointer.
@@ -223,7 +222,7 @@ class BumpPtrAllocatorImpl
void *NewSlab = Slabs[ActiveSlabIdx];
size_t AllocatedSlabSize = computeSlabSize(ActiveSlabIdx);
CurPtr = (char *)(NewSlab);
- End = ((char *)NewSlab) + AllocatedSlabSize;
+ EndSentinel = uintptr_t(NewSlab) + AllocatedSlabSize + 1;
} else
// Otherwise, start a new slab and try again.
StartNewSlab();
@@ -259,7 +258,7 @@ class BumpPtrAllocatorImpl
size_t GetNumSlabs() const { return Slabs.size() + CustomSizedSlabs.size(); }
SlabCheckPoint checkPoint() const {
- return {ActiveSlabIdx, CurPtr, End, BytesAllocated};
+ return {ActiveSlabIdx, CurPtr, EndSentinel};
}
static void poisonMemory(void *Ptr, size_t Size) {
@@ -298,16 +297,19 @@ class BumpPtrAllocatorImpl
void restoreToCheckPoint(SlabCheckPoint CP) {
assert(CP.ActiveSlabIdx >= 0 && CP.ActiveSlabIdx < Slabs.size());
assert(CP.CurPtr >= (const char *)Slabs[CP.ActiveSlabIdx] &&
- CP.End == ((const char *)Slabs[CP.ActiveSlabIdx] +
- computeSlabSize(CP.ActiveSlabIdx)));
+ CP.EndSentinel == uintptr_t(Slabs[CP.ActiveSlabIdx]) +
+ computeSlabSize(CP.ActiveSlabIdx) + 1);
ActiveSlabIdx = CP.ActiveSlabIdx;
CurPtr = CP.CurPtr;
- End = CP.End;
- BytesAllocated = CP.BytesAllocated;
- llvm::outs() << "Poisoned range = [" << (void *)CurPtr << ", " << (void *)End << ")\n";
- llvm::outs() << "Poisoned End Size = [" << (void *)CurPtr << ", " << (void *)(CurPtr + (size_t)(End - CurPtr)) << ")\n";
+ EndSentinel = CP.EndSentinel;
+
+ uintptr_t EndRange =
+ uintptr_t(Slabs[CP.ActiveSlabIdx]) + computeSlabSize(CP.ActiveSlabIdx);
+
+ llvm::outs() << "Poisoned range = [" << (void *)CurPtr << ", " << (void *)(EndSentinel - 1) << "]\n";
+ llvm::outs() << "Poisoned End Size = [" << (void *)CurPtr << ", " << (void *)(CurPtr + ((EndSentinel - 1) - (uintptr_t)CurPtr)) << "]\n";
llvm::outs().flush();
- poisonMemory((void *)CurPtr, (size_t)(End - CurPtr));
+ poisonMemory((void *)CurPtr, (size_t)(EndRange - uintptr_t(CurPtr)));
for (unsigned I = ActiveSlabIdx + 1; I < Slabs.size(); ++I)
// Should we deallocate any extra slabs?
poisonMemory(Slabs[I], computeSlabSize(I));
>From 415d3654dbc45ae44e253d5a9d65bd63fcda358f Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 16 Sep 2026 12:22:58 +0530
Subject: [PATCH 04/13] Refactor code: update PR scope to decl state recovery
---
clang/include/clang/AST/ASTContext.h | 6 -
clang/include/clang/AST/ASTMutationListener.h | 6 -
clang/include/clang/AST/Decl.h | 3 -
clang/include/clang/AST/DeclBase.h | 11 -
clang/include/clang/AST/DeclCXX.h | 5 +-
.../include/clang/AST/DeclContextInternals.h | 3 -
clang/include/clang/AST/DeclTemplate.h | 5 -
clang/include/clang/AST/DeclarationName.h | 2 -
clang/include/clang/AST/RecordLayout.h | 2 -
clang/include/clang/AST/Redeclarable.h | 4 -
.../include/clang/Interpreter/ErrorRecovery.h | 547 ++---
clang/include/clang/Sema/Sema.h | 2 -
clang/lib/AST/ASTContext.cpp | 9 -
clang/lib/Frontend/MultiplexConsumer.cpp | 5 -
.../lib/Interpreter/ASTContextStateStash.cpp | 1808 -----------------
clang/lib/Interpreter/CMakeLists.txt | 2 -
clang/lib/Interpreter/IncrementalAction.h | 1 -
clang/lib/Interpreter/IncrementalParser.cpp | 2 +-
clang/lib/Interpreter/Interpreter.cpp | 24 +-
clang/lib/Interpreter/SemaStateStash.cpp | 918 ---------
20 files changed, 203 insertions(+), 3162 deletions(-)
delete mode 100644 clang/lib/Interpreter/ASTContextStateStash.cpp
delete mode 100644 clang/lib/Interpreter/SemaStateStash.cpp
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index aaefe14fa828d..e3d09c8257d83 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -85,7 +85,6 @@ template <> struct DenseMapInfo<ScalableVecTyKey> {
namespace clang {
class APValue;
-class ASTContextStateRecovery;
class ASTMutationListener;
class ASTRecordLayout;
class AtomicExpr;
@@ -614,7 +613,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
template <class> friend class serialization::AbstractTypeReader;
friend class CXXRecordDecl;
friend class IncrementalParser;
- friend class ASTContextStateRecovery;
/// A mapping to contain the template or declaration that
/// a variable declaration describes or was instantiated from,
@@ -1362,10 +1360,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
TUDecl = NewTUDecl;
}
- void setTranslationUnitDecl(TranslationUnitDecl *NewTUDecl) {
- TUDecl = NewTUDecl;
- }
-
ExternCContextDecl *getExternCContextDecl() const;
#define BuiltinTemplate(BTName) BuiltinTemplateDecl *get##BTName##Decl() const;
diff --git a/clang/include/clang/AST/ASTMutationListener.h b/clang/include/clang/AST/ASTMutationListener.h
index 14cb3b1d14612..7540b7f90ee86 100644
--- a/clang/include/clang/AST/ASTMutationListener.h
+++ b/clang/include/clang/AST/ASTMutationListener.h
@@ -195,12 +195,6 @@ class ASTMutationListener {
virtual void AddedAnonymousNamespace(const TranslationUnitDecl *TU,
NamespaceDecl *AnonNamespace) {}
- /// A TagDecl's cached TypeForDecl was materialized for the first time.
- ///
- /// \param TD The declaration whose type cache was populated.
- /// \param T The Type now cached. Never null.
- virtual void AddedTagDeclType(const TagDecl *TD, const Type *T) {}
-
// NOTE: If new methods are added they should also be added to
// MultiplexASTMutationListener.
};
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 05a102d584a57..c64dc8c02d40e 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -57,7 +57,6 @@
namespace clang {
class ASTContext;
-class ASTStateReader;
struct ASTTemplateArgumentListInfo;
class CompoundStmt;
class DependentFunctionTemplateSpecializationInfo;
@@ -3649,7 +3648,6 @@ class IndirectFieldDecl : public ValueDecl,
class TypeDecl : public NamedDecl {
friend class ASTContext;
friend class ASTReader;
- friend class ASTStateReader;
/// This indicates the Type object that represents
/// this TypeDecl. It is a cache maintained by
@@ -3916,7 +3914,6 @@ class TagDecl : public TypeDecl,
public:
friend class ASTDeclReader;
- friend class ASTStateReader;
friend class ASTDeclWriter;
using redecl_range = redeclarable_base::redecl_range;
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 9dd1f60011ce0..8ea2533825dd9 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -43,13 +43,10 @@
namespace clang {
class ASTContext;
-class ASTContextStateRecovery;
-class ASTDeclDetacher;
class ASTMutationListener;
class Attr;
class BlockDecl;
class DeclContext;
-class DeclContextRepairer;
class ExternalSourceSymbolAttr;
class FunctionDecl;
class FunctionType;
@@ -261,9 +258,6 @@ class alignas(8) Decl {
private:
friend class DeclContext;
- // friend class ASTContextStateRecovery;
- friend class ASTDeclDetacher;
- friend class DeclContextRepairer;
struct MultipleDC {
DeclContext *SemanticDC;
@@ -1351,7 +1345,6 @@ namespace clang {
/// A list storing NamedDecls in the lookup tables.
class DeclListNode {
friend class ASTContext; // allocate, deallocate nodes.
- friend class ASTContextStateRecovery;
friend class StoredDeclsList;
public:
using Decls = llvm::PointerUnion<NamedDecl*, DeclListNode*>;
@@ -1471,10 +1464,6 @@ enum class LinkageSpecLanguageIDs;
/// BlockDecl
/// CapturedDecl
class DeclContext {
- /// For restoring decl to its previous state (clang-repl error-recovery).
- friend class ASTDeclDetacher;
-
- friend class DeclContextRepairer;
/// For makeDeclVisibleInContextImpl
friend class ASTDeclReader;
/// For checking the new bits in the Serialization part.
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 539cf28a76031..74122971a1492 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -54,8 +54,7 @@
namespace clang {
class ASTContext;
-class ASTDeclDetacher;
-class ASTStateReader;
+class DeclStateReverter;
class ClassTemplateDecl;
class ConstructorUsingShadowDecl;
class CXXBasePath;
@@ -265,9 +264,9 @@ class CXXRecordDecl : public RecordDecl {
friend class ASTNodeImporter;
friend class ASTReader;
friend class ASTRecordWriter;
- friend class ASTStateReader;
friend class ASTWriter;
friend class DeclContext;
+ friend class DeclStateReverter;
friend class LambdaExpr;
friend class ODRDiagsEmitter;
diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h
index e7d9c2d742dc8..87820b14d6188 100644
--- a/clang/include/clang/AST/DeclContextInternals.h
+++ b/clang/include/clang/AST/DeclContextInternals.h
@@ -301,13 +301,10 @@ class StoredDeclsList {
}
};
-class ASTContextStateRecovery;
-
class StoredDeclsMap
: public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> {
friend class ASTContext; // walks the chain deleting these
friend class DeclContext;
- friend class ASTContextStateRecovery;
llvm::PointerIntPair<StoredDeclsMap*, 1> Previous;
public:
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index a4beffb99465b..fd1c7df6e7813 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -46,8 +46,6 @@
namespace clang {
-class ASTDeclDetacher;
-class ASTStateReader;
enum BuiltinTemplateKind : int;
class ClassTemplateDecl;
class ClassTemplatePartialSpecializationDecl;
@@ -826,9 +824,7 @@ class RedeclarableTemplateDecl : public TemplateDecl,
public:
friend class ASTDeclReader;
friend class ASTDeclWriter;
- friend class ASTDeclDetacher;
friend class ASTReader;
- friend class ASTStateReader;
template <class decl_type> friend class RedeclarableTemplate;
@@ -2317,7 +2313,6 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
friend class ASTDeclReader;
friend class ASTDeclWriter;
- friend class ASTStateReader;
friend class TemplateDeclInstantiator;
/// Load any lazily-loaded specializations from the external source.
diff --git a/clang/include/clang/AST/DeclarationName.h b/clang/include/clang/AST/DeclarationName.h
index dd490c688de18..5121b561288c0 100644
--- a/clang/include/clang/AST/DeclarationName.h
+++ b/clang/include/clang/AST/DeclarationName.h
@@ -32,7 +32,6 @@
namespace clang {
class ASTContext;
-class ASTContextStateRecovery;
template <typename> class CanQual;
class DeclarationName;
class DeclarationNameTable;
@@ -592,7 +591,6 @@ inline bool operator>=(DeclarationName LHS, DeclarationName RHS) {
/// uniqued versions of each of the C++ special names, which can be
/// retrieved using its member functions (e.g., getCXXConstructorName).
class DeclarationNameTable {
- friend class ASTContextStateRecovery;
/// Used to allocate elements in the FoldingSets below.
const ASTContext &Ctx;
diff --git a/clang/include/clang/AST/RecordLayout.h b/clang/include/clang/AST/RecordLayout.h
index a7964b69feb03..9af17df229b37 100644
--- a/clang/include/clang/AST/RecordLayout.h
+++ b/clang/include/clang/AST/RecordLayout.h
@@ -26,7 +26,6 @@
namespace clang {
class ASTContext;
-class ASTContextStateRecovery;
class CXXRecordDecl;
/// ASTRecordLayout -
@@ -61,7 +60,6 @@ class ASTRecordLayout {
private:
friend class ASTContext;
- friend class ASTContextStateRecovery;
/// Size - Size of record in characters.
CharUnits Size;
diff --git a/clang/include/clang/AST/Redeclarable.h b/clang/include/clang/AST/Redeclarable.h
index 150c310530c03..fe2648aa58301 100644
--- a/clang/include/clang/AST/Redeclarable.h
+++ b/clang/include/clang/AST/Redeclarable.h
@@ -25,9 +25,7 @@
namespace clang {
class ASTContext;
-class ASTDeclDetacher;
class Decl;
-class DeclContextRepairer;
// Some notes on redeclarables:
//
@@ -191,8 +189,6 @@ class Redeclarable {
friend class ASTDeclUnmerger;
friend class ASTDeclReader;
friend class ASTDeclWriter;
- friend class ASTDeclDetacher;
- friend class DeclContextRepairer;
friend class IncrementalParser;
Redeclarable(const ASTContext &Ctx)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 0c47e9bc2296b..2ef75b2492fa2 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -24,309 +24,6 @@
namespace clang {
class ASTContext;
-struct SemaStashCheckPoint {
- /// Sema::*
- llvm::SlabCheckPoint SemaBumpSlabCP;
- size_t CachedFunctionScopeSize = 0;
- size_t FunctionScopesSize = 0;
- size_t Ident_superSize = 0;
- size_t PragmaClangBSSSectionSize = 0;
- size_t PragmaClangDataSectionSize = 0;
- size_t PragmaClangRodataSectionSize = 0;
- size_t PragmaClangRelroSectionSize = 0;
- size_t PragmaClangTextSectionSize = 0;
- size_t VtorDispStackSize = 0;
- size_t AlignPackStackSize = 0;
- size_t AlignPackIncludeStackSize = 0;
- size_t DataSegStackSize = 0;
- size_t BSSSegStackSize = 0;
- size_t ConstSegStackSize = 0;
- size_t CodeSegStackSize = 0;
- size_t StrictGuardStackCheckStackSize = 0;
- size_t FpPragmaStackSize = 0;
- size_t FunctionToSectionMapSize = 0;
- size_t PragmaAttributeStackSize = 0;
- size_t MSFunctionNoBuiltinsSize = 0;
- size_t PendingExportedNamesSize = 0;
- size_t TypoCorrectedFunctionDefinitionsSize = 0;
- size_t FlagBitsCacheSize = 0;
- size_t AssignEnumCacheSize = 0;
- size_t WeakUndeclaredIdentifiersSize = 0;
- size_t ExtnameUndeclaredIdentifiersSize = 0;
- size_t UnusedLocalTypedefNameCandidatesSize = 0;
- Sema::UnusedFileScopedDeclsType::iterator UnusedFileScopedDeclsSize;
- Sema::TentativeDefinitionsType::iterator TentativeDefinitionsSize;
- size_t ExternalDeclarationsSize = 0;
- size_t ParsingInitForAutoVarsSize = 0;
- size_t DeclsToCheckForDeferredDiagsSize = 0;
- size_t ShadowingDeclsSize = 0;
- size_t WeakTopLevelDeclSize = 0;
- Sema::ExtVectorDeclsType::iterator ExtVectorDeclsSize;
- size_t VTableUsesSize = 0;
- size_t VTablesUsedSize = 0;
- size_t DelayedDllExportClassesSize = 0;
- size_t DelayedDllExportMemberFunctionsSize = 0;
- size_t InventedParameterInfosSize = 0;
- size_t FieldCollectorSize = 0;
- size_t UnusedPrivateFieldsSize = 0;
- size_t PureVirtualClassDiagSetSize = 0;
- Sema::DelegatingCtorDeclsType::iterator DelegatingCtorDeclsSize;
- size_t StdNamespaceSize = 0;
- size_t UnparsedDefaultArgLocsSize = 0;
- size_t UndefinedButUsedSize = 0;
- size_t SpecialMembersBeingDeclaredSize = 0;
- size_t DelayedOverridingExceptionSpecChecksSize = 0;
- size_t DelayedEquivalentExceptionSpecChecksSize = 0;
- size_t MaybeODRUseExprsSize = 0;
- size_t RefsMinusAssignmentsSize = 0;
- size_t ExprCleanupObjectsSize = 0;
- size_t ExprEvalContextsSize = 0;
- size_t FailedImmediateInvocationsSize = 0;
- size_t ImplicitlyRetainedSelfLocsSize = 0;
- size_t DeleteExprsSize = 0;
- size_t CurrentParameterCopyTypesSize = 0;
- size_t AggregateDeductionCandidatesSize = 0;
- size_t TypoCorrectionFailuresSize = 0;
- size_t SpecialMemberCacheSize = 0;
- size_t ModuleScopesSize = 0;
- size_t DeferredExportedNamespacesSize = 0;
- size_t PendingInlineFuncDeclsSize = 0;
- size_t CurrentSEHFinallySize = 0;
- size_t CurrentDeferSize = 0;
- size_t LateParsedTemplateMapSize = 0;
- size_t SuppressedDiagnosticsSize = 0;
- size_t CurrentInstantiationScopeSize = 0;
- size_t UnparsedDefaultArgInstantiationsSize = 0;
- size_t CodeSynthesisContextsSize = 0;
- size_t InstantiatingSpecializationsSize = 0;
- size_t InstantiatedNonDependentTypesSize = 0;
- size_t CodeSynthesisContextLookupModulesSize = 0;
- size_t LookupModulesCacheSize = 0;
- size_t VisibleNamespaceCacheSize = 0;
- // size_t TemplateInstCallbacksSize = 0;
- size_t PendingInstantiationsSize = 0;
- size_t LateParsedInstantiationsSize = 0;
- size_t SavedVTableUsesSize = 0;
- size_t SavedPendingInstantiationsSize = 0;
- size_t PendingLocalImplicitInstantiationsSize = 0;
- size_t CurrentCachedTemplateArgsSize = 0;
- size_t UnsubstitutedConstraintSatisfactionCacheSize = 0;
- size_t SubsumptionCacheSize = 0;
- size_t NormalizationCacheSize = 0;
- size_t SatisfactionCacheSize = 0;
- size_t SatisfactionStackSize = 0;
- // size_t NullabilityMapSize = 0;
- size_t DeclsWithEffectsToVerifySize = 0;
- // size_t AllEffectsToVerifySize = 0;
-};
-
-/// Stashes and restores persistent Sema state around an incremental parse.
-///
-/// Usage:
-/// SemaStashCheckPoint CP;
-/// SemaStateRecovery Stash(S);
-/// Stash.stash(CP);
-/// // ... parse ...
-/// if (failed)
-/// Stash.restore(CP, ASTSlabCP);
-class SemaStateRecovery {
- Sema &S;
-
- /// Holds full copies of PragmaStack, PragmaClangSection, FileNullabilityMap,
- /// etc.
- // struct PragmaSnapshot;
- // std::unique_ptr<PragmaSnapshot> Pragmas;
-
-public:
- explicit SemaStateRecovery(Sema &S) : S(S) {}
- // ~SemaStateRecovery();
-
- void stash(SemaStashCheckPoint &CP);
- void restore(SemaStashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
-};
-
-struct StashCheckPoint {
- // Types vector
- size_t TypesSize = 0;
-
- // FoldingSets — store count of nodes
- size_t ExtQualNodesSize = 0;
- size_t ComplexTypesSize = 0;
- size_t PointerTypesSize = 0;
- size_t AdjustedTypesSize = 0;
- size_t BlockPointerTypesSize = 0;
- size_t LValueReferenceTypesSize = 0;
- size_t RValueReferenceTypesSize = 0;
- size_t MemberPointerTypesSize = 0;
-
- size_t ConstantArrayTypesSize = 0;
- size_t IncompleteArrayTypesSize = 0;
- size_t VariableArrayTypesSize = 0;
-
- size_t DependentSizedArrayTypesSize = 0;
- size_t DependentSizedExtVectorTypesSize = 0;
- size_t DependentAddressSpaceTypesSize = 0;
- size_t VectorTypesSize = 0;
- size_t DependentVectorTypesSize = 0;
- size_t MatrixTypesSize = 0;
- size_t DependentSizedMatrixTypesSize = 0;
- size_t FunctionNoProtoTypesSize = 0;
- size_t FunctionProtoTypesSize = 0;
- size_t DependentTypeOfExprTypesSize = 0;
- size_t DependentDecltypeTypesSize = 0;
-
- size_t DependentPackIndexingTypesSize = 0;
-
- size_t TemplateTypeParmTypesSize = 0;
- size_t ObjCTypeParamTypesSize = 0;
- size_t SubstTemplateTypeParmTypesSize = 0;
- size_t SubstTemplateTypeParmPackTypesSize = 0;
- size_t SubstBuiltinTemplatePackTypesSize = 0;
-
- size_t TemplateSpecializationTypesSize = 0;
- size_t ParenTypesSize = 0;
- size_t TagTypesSize = 0;
- size_t UnresolvedUsingTypesSize = 0;
- size_t UsingTypesSize = 0;
- size_t TypedefTypesSize = 0;
- size_t DependentNameTypesSize = 0;
- size_t PackExpansionTypesSize = 0;
- size_t UnaryTransformTypesSize = 0;
-
- size_t AutoTypesSize = 0;
- size_t DeducedTemplateSpecializationTypesSize = 0;
- size_t AtomicTypesSize = 0;
- size_t AttributedTypesSize = 0;
- size_t PipeTypesSize = 0;
- size_t BitIntTypesSize = 0;
- size_t DependentBitIntTypesSize = 0;
- size_t BTFTagAttributedTypesSize = 0;
- size_t HLSLAttributedResourceTypesSize = 0;
- size_t HLSLInlineSpirvTypesSize = 0;
-
- size_t CountAttributedTypesSize = 0;
-
- size_t QualifiedTemplateNamesSize = 0;
- size_t DependentTemplateNamesSize = 0;
- size_t SubstTemplateTemplateParmsSize = 0;
- size_t SubstTemplateTemplateParmPacksSize = 0;
- size_t DeducedTemplatesSize = 0;
-
- size_t ArrayParameterTypesSize = 0;
-
- size_t PredefinedSugarTypesSize = 0;
-
- size_t NamespaceAndPrefixStoragesSize = 0;
-
- size_t ASTRecordLayoutsSize = 0;
-
- size_t MemoizedTypeInfoSize = 0;
-
- size_t MemoizedUnadjustedAlignSize = 0;
-
- size_t KeyFunctionsSize = 0;
-
- size_t BlockVarCopyInitsSize = 0;
-
- size_t MSGuidDeclsSize = 0;
-
- size_t UnnamedGlobalConstantDeclsSize = 0;
-
- size_t TemplateParamObjectDeclsSize = 0;
-
- size_t StringLiteralCacheSize = 0;
-
- size_t DestroyingOperatorDeletesSize = 0;
- size_t TypeAwareOperatorNewAndDeletesSize = 0;
-
- size_t OperatorDeletesForVirtualDtorSize = 0;
-
- size_t GlobalOperatorDeletesForVirtualDtorSize = 0;
-
- size_t ArrayOperatorDeletesForVirtualDtorSize = 0;
- size_t GlobalArrayOperatorDeletesForVirtualDtorSize = 0;
-
- size_t MaybeRequireVectorDeletingDtorSize = 0;
-
- size_t MergedDeclsSize = 0;
- size_t DeclAttrsSize = 0;
- size_t MergedDefModulesSize = 0;
-
- size_t ModuleInitializersSize = 0;
- size_t PrimaryModuleNameMapSize = 0;
- size_t SameModuleLookupSetSize = 0;
-
- size_t ScalableVecTyMapSize = 0;
- size_t LambdaCastPathsSize = 0;
- size_t RawCommentsSize = 0;
- size_t RedeclChainCommentsSize = 0;
- size_t CommentlessRedeclChainsSize = 0;
- size_t ParsedCommentsSize = 0;
- size_t RelocatableClassesSize = 0;
- size_t ParamIndicesSize = 0;
- size_t MangleNumbersSize = 0;
- size_t StaticLocalNumbersSize = 0;
- size_t TemplateOrInstantiationSize = 0;
-
- size_t InstantiatedFromUsingDeclSize = 0;
- size_t InstantiatedFromUsingEnumDeclSize = 0;
-
- size_t InstantiatedFromUsingShadowDeclSize = 0;
-
- size_t InstantiatedFromUnnamedFieldDeclSize = 0;
- size_t OverriddenMethodsSize = 0;
- size_t MangleNumberingContextsSize = 0;
- size_t ExtraMangleNumberingContextsSize = 0;
-
- size_t TraversalScopeSize = 0;
-
- /// object-c
- size_t ObjCObjectTypesSize = 0;
- size_t ObjCObjectPointerTypesSize = 0;
-
- mutable TypedefDecl *ObjCIdDeclCP = nullptr;
-
- mutable TypedefDecl *ObjCSelDeclCP = nullptr;
-
- mutable TypedefDecl *ObjCClassDeclCP = nullptr;
-
- mutable ObjCInterfaceDecl *ObjCProtocolClassDeclCP = nullptr;
-
- // llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
- // = llvm::PointerIntPair<StoredDeclsMap *, 1>(nullptr, 0);
- mutable QualType AutoDeductTy; // Deduction against 'auto'.
- mutable QualType AutoRRefDeductTy; // Deduction against 'auto &&'.
-
- // mutable DeclarationNameTable DeclarationNames; need to revert.
- struct DeclarationNamesCP {
- size_t CXXConstructorNamesSize = 0;
- size_t CXXDestructorNamesSize = 0;
- size_t CXXConversionFunctionNamesSize = 0;
- detail::CXXOperatorIdName CXXOperatorNamesCP[NUM_OVERLOADED_OPERATORS];
-
- size_t CXXLiteralOperatorNamesSize = 0;
-
- size_t CXXDeductionGuideNamesSize = 0;
- };
-
- DeclarationNamesCP DeclarationNames;
-};
-
-class ASTContextStateRecovery {
-private:
- ASTContext &Ctx;
-
-public:
- explicit ASTContextStateRecovery(ASTContext &Ctx) : Ctx(Ctx) {}
-
- ASTContextStateRecovery(const ASTContextStateRecovery &) = delete;
- ASTContextStateRecovery &operator=(const ASTContextStateRecovery &) = delete;
-
- void stash(StashCheckPoint &CP);
- void restore(StashCheckPoint &CP, llvm::SlabCheckPoint SlabCP);
- void commit();
-};
-
/// Index of a per PTU State.
using PTUID = unsigned;
@@ -572,8 +269,77 @@ struct MemberSpecializationFootprint {
}
};
-class ASTStateReader {
-public:
+class DeclStateReverter {
+private:
+ struct TemplateCommonAccess : RedeclarableTemplateDecl {
+ using RedeclarableTemplateDecl::Common;
+ };
+
+ static const void *rawCommonPtr(const RedeclarableTemplateDecl &RT) {
+ return static_cast<const TemplateCommonAccess &>(RT).Common;
+ }
+
+ // Common is `mutable`, so this is legal to call through a const
+ // reference too -- no const_cast needed at any call site.
+ static void clearCommonPtr(const RedeclarableTemplateDecl &RT) {
+ static_cast<const TemplateCommonAccess &>(RT).Common = nullptr;
+ }
+
+ struct ClassTemplateCommonAccess : ClassTemplateDecl {
+ using ClassTemplateDecl::getCommonPtr;
+ };
+
+ static bool canonInjectedTSTValid(const ClassTemplateDecl &CTD) {
+ auto *Ptr =
+ static_cast<const ClassTemplateCommonAccess &>(CTD).getCommonPtr();
+ return !Ptr->CanonInjectedTST.isNull();
+ }
+
+ static void clearCanonInjectedTST(ClassTemplateDecl &CTD) {
+ auto *Ptr = static_cast<ClassTemplateCommonAccess &>(CTD).getCommonPtr();
+ Ptr->CanonInjectedTST = CanQualType();
+ }
+
+ // DeclContext::FirstDecl/LastDecl -- protected.
+ struct DeclContextLinkAccess : DeclContext {
+ using DeclContext::FirstDecl;
+ using DeclContext::LastDecl;
+ };
+
+ static void clearDeclContextChain(DeclContext &DC) {
+ auto &Access = static_cast<DeclContextLinkAccess &>(DC);
+ Access.FirstDecl = nullptr;
+ Access.LastDecl = nullptr;
+ }
+
+ struct TagDeclDefinitionAccess : TagDecl {
+ using TagDecl::setBeingDefined;
+ };
+
+ static void clearBeingDefined(TagDecl &TD) {
+ static_cast<TagDeclDefinitionAccess &>(TD).setBeingDefined(false);
+ }
+
+ struct DeclLexicalLinkAccess : Decl {
+ using Decl::NextInContextAndBits;
+ };
+
+ static void clearNextInContext(Decl &D) {
+ static_cast<DeclLexicalLinkAccess &>(D).NextInContextAndBits.setPointer(
+ nullptr);
+ }
+
+ template <typename decl_type>
+ struct RedeclLinkAccess : Redeclarable<decl_type> {
+ using Redeclarable<decl_type>::RedeclLink;
+ };
+
+ template <typename decl_type>
+ static void setLatestRedecl(Redeclarable<decl_type> &D, decl_type *Latest) {
+ static_cast<RedeclLinkAccess<decl_type> &>(D).RedeclLink.setLatest(Latest);
+ }
+
+protected:
static DefinitionDataFootprint *
createDefinitionDataFootprint(const ASTContext &Ctx, const CXXRecordDecl &RD);
@@ -585,8 +351,12 @@ class ASTStateReader {
static SpecializationFootprint *
createSpecializationFootprint(const ASTContext &Ctx,
- const ClassTemplateSpecializationDecl &Spec);
-
+ const ClassTemplateSpecializationDecl &Spec) {
+ auto *FP =
+ new (Ctx, alignof(SpecializationFootprint)) SpecializationFootprint();
+ FP->update(Spec);
+ return FP;
+ }
static bool
compareSpecializationFootprint(const SpecializationFootprint &FP,
const ClassTemplateSpecializationDecl &Spec) {
@@ -602,8 +372,12 @@ class ASTStateReader {
static VarSpecializationFootprint *
createVarSpecializationFootprint(const ASTContext &Ctx,
- const VarTemplateSpecializationDecl &Spec);
-
+ const VarTemplateSpecializationDecl &Spec) {
+ auto *FP = new (Ctx, alignof(VarSpecializationFootprint))
+ VarSpecializationFootprint();
+ FP->update(Spec);
+ return FP;
+ }
static bool
compareVarSpecializationFootprint(const VarSpecializationFootprint &FP,
const VarTemplateSpecializationDecl &Spec) {
@@ -619,11 +393,18 @@ class ASTStateReader {
static FunctionSpecializationFootprint *
createFunctionSpecializationFootprint(const ASTContext &Ctx,
- const FunctionDecl &FD);
-
+ const FunctionDecl &FD) {
+ auto *FP = new (Ctx, alignof(FunctionSpecializationFootprint))
+ FunctionSpecializationFootprint();
+ FP->update(FD);
+ return FP;
+ }
static bool compareFunctionSpecializationFootprint(
- const FunctionSpecializationFootprint &FP, const FunctionDecl &FD);
-
+ const FunctionSpecializationFootprint &FP, const FunctionDecl &FD) {
+ FunctionSpecializationFootprint Live;
+ Live.update(FD);
+ return FP == Live;
+ }
static void restoreFunctionSpecializationFootprint(
const FunctionSpecializationFootprint &FP, FunctionDecl &FD) {
FP.restore(FD);
@@ -631,32 +412,115 @@ class ASTStateReader {
template <typename OwnerT>
static MemberSpecializationFootprint *
- createMemberSpecializationFootprint(const ASTContext &Ctx, const OwnerT &D);
-
+ createMemberSpecializationFootprint(const ASTContext &Ctx, const OwnerT &D) {
+ auto *FP = new (Ctx, alignof(MemberSpecializationFootprint))
+ MemberSpecializationFootprint();
+ FP->update(D);
+ return FP;
+ }
template <typename OwnerT>
static bool
compareMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
- const OwnerT &D);
+ const OwnerT &D) {
+ MemberSpecializationFootprint Live;
+ Live.update(D);
+ return FP == Live;
+ }
template <typename OwnerT>
static void
restoreMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
- OwnerT &D);
+ OwnerT &D) {
+ FP.restore(D);
+ }
static void restoreDefinitionAndRevertDC(CXXRecordDecl &RD);
static void revertDefinitionArrival(Decl &D);
- static const void *getRawCommonPtr(const RedeclarableTemplateDecl &RT);
+ // True if Common could still be created later -- i.e. nobody has called
+ // getCommonPtr() anywhere in this template's redecl chain yet.
+ static bool needToTrackCommonPtr(const RedeclarableTemplateDecl &RT) {
+ return !rawCommonPtr(RT);
+ }
- static bool isTemplateCanonInjectedTSTValid(const ClassTemplateDecl *CTD);
+ // True if CanonInjectedTST could still be cached later. Only valid to
+ // call once Common itself is confirmed to exist (see needToTrackCommonPtr).
+ static bool
+ needToTrackTemplateCanonInjectedTST(const ClassTemplateDecl *CTD) {
+ return !canonInjectedTSTValid(*CTD);
+ }
+
+ // Write-once fields, fixed null prior state (Common/CanonInjectedTST
+ // never existed before whichever PTU created them), so revert is a
+ // direct reset, not a snapshot restore.
+ static void resetTemplateCommonBase(RedeclarableTemplateDecl &RT) {
+ clearCommonPtr(RT);
+ }
+
+ static void resetCanonInjectedTST(ClassTemplateDecl &CTD) {
+ clearCanonInjectedTST(CTD);
+ }
+
+ // static const Type *getRawTypeForDecl(const TypeDecl *TD) {
+ // return TD->TypeForDecl;
+ // }
+
+ // static bool needToTrackTypeForDecl(const TypeDecl *TD) {
+ // const Type *T = getRawTypeForDecl(TD);
+ // return !T || T->isCanonicalUnqualified();
+ // }
- static void resetTemplateCommonBase(RedeclarableTemplateDecl &RT);
+ // static void resetTypeForDecl(TypeDecl *TD) { TD->TypeForDecl = nullptr; }
- static void resetCanonInjectedTST(ClassTemplateDecl &CTD);
+private:
+ ASTContext &Ctx;
+ llvm::SlabCheckPoint SlabCP;
+ llvm::SmallPtrSet<const DeclContext *, 8> RepairedLexicalContexts;
- static const Type *getRawTypeForDecl(const TypeDecl *TD);
+ bool isAfterCP(const void *P) const {
+ return Ctx.getAllocator().isAfterCheckpoint(P, SlabCP);
+ }
+
+public:
+ DeclStateReverter(ASTContext &Ctx, llvm::SlabCheckPoint CP)
+ : Ctx(Ctx), SlabCP(CP) {}
+
+ static bool isExtensibleContainer(const Decl *D) {
+ return isa<NamespaceDecl>(D) || isa<CXXRecordDecl>(D);
+ }
+
+ static bool isRedeclarableOrOnlyDecl(Decl *D) {
+ return D->getPreviousDecl() != nullptr;
+ }
- static void resetTypeForDecl(TypeDecl *TD);
+ void detachDefData(const Decl *D);
+
+ void detachCommonBase(const Decl *D);
+
+ /// Remove D from its semantic context's lookup map, reinstating the
+ /// previous declaration if D had replaced one in-place (which is what
+ /// StoredDeclsList::HandleRedeclaration does on a redeclaration --
+ /// erasing the slot outright would lose the older decl entirely; that
+ /// is the ReopenNs failure).
+ void detachFromDCLookup(const Decl *D);
+
+ // Walk D's redecl chain looking for the newest decl that predates this
+ // PTU. Returns nullptr if the entire chain was created this PTU.
+ template <typename DeclT> DeclT *findSurvivor(DeclT *D) const;
+
+ template <typename decl_type>
+ void patchRedeclLink(Redeclarable<decl_type> *D, decl_type *Survivor) {
+ setLatestRedecl<decl_type>(*D->getFirstDecl(), Survivor);
+ }
+
+ /// Point the canonical decl's "most recent" link back at the newest
+ /// redeclaration that predates this PTU.
+ void detachFromRedeclChain(const Decl *D);
+
+ void repairLexicalChain(DeclContext &DC);
+
+private:
+ NamedDecl *tryDetachRedeclChain(Decl *D);
};
template <typename DataT> struct Snapshot {
@@ -714,12 +578,6 @@ using SpecializationChain = StateAwareChain<SpecializationFootprint>;
using VarSpecializationChain = StateAwareChain<VarSpecializationFootprint>;
using FunctionSpecializationChain =
StateAwareChain<FunctionSpecializationFootprint>;
-// One chain type shared by all four MemberSpecializationInfo-backed owner
-// kinds (Function/Var/Record/Enum) -- the footprint shape is identical
-// across all four (see MemberSpecializationFootprint's own comment), so
-// unlike the three chains above, a single alias is enough; what varies is
-// only the DenseMap key type in IncrementalStateTracker
-// (FunctionDecl*/VarDecl*/ CXXRecordDecl*/EnumDecl*), one map per owner kind.
using MemberSpecializationChain =
StateAwareChain<MemberSpecializationFootprint>;
@@ -782,8 +640,6 @@ class PTUCheckpointLedger {
}
};
-/// need to handle this TagDeclBitfields TagDeclBits;
-
template <typename ValueT> struct FieldMutation {
PTUID ID;
ValueT OldValue;
@@ -848,14 +704,8 @@ template <typename OwnerT, typename ValueT> class FieldMutationChain {
void forget(const OwnerT *Owner) { Log.erase(Owner); }
};
-// struct FunctionTypeEntry {
-// QualType Type;
-// QualType OverridingType;
-// };
using FunctionExceptionSpecChain = FieldMutationChain<FunctionDecl, QualType>;
using TypeForDeclChain = FieldMutationChain<TagDecl, const Type *>;
-// using CanonInjectedTSTChain =
-// FieldMutationChain<ClassTemplateDecl, CanQualType>;
struct MutationRecord {
enum class DeclShape : uint16_t {
@@ -908,12 +758,6 @@ struct PTUStateInfo {
llvm::MapVector<const Decl *, MutationRecord> Mutations;
- // struct CreationRecord {
- // DeclShape S;
- // CreatedType Type;
- // };
-
- // llvm::MapVector<const Decl *, CreationRecord> CreatedDecls;
llvm::SmallPtrSet<const Decl *, 4> ImplicitDecls;
/// here touched info mean other this belongs to other PTUs;
@@ -1201,7 +1045,6 @@ class IncrementalStateTracker {
FunctionExceptionSpecChain FunctionTypeMutations;
TypeForDeclChain TagdeclInfos;
- // CanonInjectedTSTChain;
friend class PTUMutationActions;
@@ -1362,6 +1205,7 @@ class IncrementalStateTracker {
}
PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
+
bool isFromThisPTU(const void *Ptr, PTUID ID) {
return PTUSlabCheckpoints.isFromThisPTU(Ctx, Ptr, ID);
}
@@ -1373,10 +1217,9 @@ class IncrementalStateTracker {
return It->second;
}
- // Pops this PTU's own tail entry (if any) from *Field. If the chain is
- // now empty -- meaning everything it ever held belonged to the PTU being
- // rolled back -- releases it back to its pool and nulls the pointer.
- // Returns whether the chain still has surviving (earlier-PTU) history.
+ void undoLastEntries();
+
+private:
template <typename ChainT>
static bool rollbackChainField(ChainT *&Field, PTUID ID,
LinkedDeclNodeGenerator &Gen);
@@ -1387,7 +1230,6 @@ class IncrementalStateTracker {
PTUID ID, LinkedDeclNodeGenerator &Gen);
void removeLinkedDecl(const Decl *D, PTUID ID);
// just only remove entries from current();
- void undoLastEntries();
};
class PTUMutationActions {
@@ -1468,7 +1310,8 @@ class PTUMutationActions {
/// EvaluatedValueCached likewise) is never returned as verified --
/// there is nothing to compare it against, so it cannot be confirmed,
/// full stop, not assumed either way.
- // static uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds,
+ // static uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t
+ // FlaggedKinds,
// PTUID ID);
void commitLevel1(PTUID ID, const Decl *D, MutationRecord &Rec,
@@ -1588,13 +1431,11 @@ class PTUMutationRecorder : public ASTMutationListener {
/// A templated variable's definition was implicitly instantiated.
void VariableDefinitionInstantiated(const VarDecl *D) override {
noteDefinitionInstantiated(D);
- // handle Memberinfo
}
/// A function template's definition was instantiated.
void FunctionDefinitionInstantiated(const FunctionDecl *D) override {
noteDefinitionInstantiated(D);
- // handle Memberinfo
}
/// A default argument was instantiated.
@@ -1678,8 +1519,6 @@ class PTUMutationRecorder : public ASTMutationListener {
/// \param AnonNamespace The anonymous namespace that was added
void AddedAnonymousNamespace(const TranslationUnitDecl *TU,
NamespaceDecl *AnonNamespace) override {}
-
- void AddedTagDeclType(const TagDecl *TD, const Type *T) override {}
};
} // end namespace clang
#endif // LLVM_CLANG_INTERPRETER_ERROR_RECOVERY_H
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 7e20dbf371b20..e33073c652dba 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -186,7 +186,6 @@ class SemaSystemZ;
class SemaWasm;
class SemaX86;
class StandardConversionSequence;
-class SemaStateRecovery;
class TemplateArgument;
class TemplateArgumentLoc;
class TemplateInstantiationCallback;
@@ -1588,7 +1587,6 @@ class Sema final : public SemaBase {
friend class ASTReader;
friend class ASTDeclReader;
friend class ASTWriter;
- friend class SemaStateRecovery;
private:
std::optional<std::unique_ptr<DarwinSDKInfo>> CachedDarwinSDKInfo;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index f0d760845d3b4..a361b07e201b3 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5596,10 +5596,6 @@ CanQualType ASTContext::getCanonicalTagType(const TagDecl *TD) const {
/*OwnsTag=*/false, /*IsInjected=*/false, /*CanonicalType=*/nullptr,
/*WithFoldingSetNode=*/false);
TD->TypeForDecl = CanonicalType;
-
- if (ASTMutationListener *L = getASTMutationListener())
- L->AddedTagDeclType(TD, TD->TypeForDecl);
-
return CanQualType::CreateUnsafe(QualType(CanonicalType, 0));
}
@@ -5625,12 +5621,7 @@ QualType ASTContext::getTagType(ElaboratedTypeKeyword Keyword,
/*Qualifier=*/std::nullopt, NonInjectedTD,
/*OwnsTag=*/false, IsInjected, CanonicalType,
/*WithFoldingSetNode=*/false);
-
TD->TypeForDecl = T;
-
- if (ASTMutationListener *L = getASTMutationListener())
- L->AddedTagDeclType(TD, TD->TypeForDecl);
-
return QualType(T, 0);
}
diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp
index bd9f4af9b7fd2..4d27b04248f93 100644
--- a/clang/lib/Frontend/MultiplexConsumer.cpp
+++ b/clang/lib/Frontend/MultiplexConsumer.cpp
@@ -130,7 +130,6 @@ class MultiplexASTMutationListener : public ASTMutationListener {
void AddedStaticLocalNumbers(const Decl *D, unsigned) override;
void AddedAnonymousNamespace(const TranslationUnitDecl *,
NamespaceDecl *AnonNamespace) override;
- void AddedTagDeclType(const TagDecl *TD, const Type *T) override;
private:
std::vector<ASTMutationListener*> Listeners;
@@ -279,10 +278,6 @@ void MultiplexASTMutationListener::AddedAnonymousNamespace(
for (auto *L : Listeners)
L->AddedAnonymousNamespace(TU, AnonNamespace);
}
-void MultiplexASTMutationListener::AddedTagDeclType(const TagDecl *TD, const Type *T) {
- for (auto *L : Listeners)
- L->AddedTagDeclType(TD, T);
-}
} // end namespace clang
diff --git a/clang/lib/Interpreter/ASTContextStateStash.cpp b/clang/lib/Interpreter/ASTContextStateStash.cpp
deleted file mode 100644
index d9da3f7448da8..0000000000000
--- a/clang/lib/Interpreter/ASTContextStateStash.cpp
+++ /dev/null
@@ -1,1808 +0,0 @@
-//===--- ASTContextStateRecovery.cpp - ASTContext persistent state stash/restore
-//----------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Interpreter/ErrorRecovery.h"
-
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/DeclContextInternals.h"
-#include "clang/AST/DeclTemplate.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/ExprCXX.h"
-#include "clang/AST/MangleNumberingContext.h"
-#include "clang/AST/RecordLayout.h"
-#include "clang/AST/Type.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include "llvm/ADT/SmallSet.h"
-#include "llvm/ADT/SmallVector.h"
-
-namespace clang {
-
-/// Erases nodes from a FoldingSet based on a predicate.
-template <typename EntryType, typename PredT>
-static void eraseFoldingSetIf(llvm::UniquingSet<EntryType> &FS, PredT &&Pred) {
- SmallVector<EntryType *, 16> ToRemove;
- for (auto &N : FS)
- if (Pred(N))
- ToRemove.push_back(&N);
-
- for (auto *N : ToRemove)
- FS.erase(N);
-}
-
-template <typename EntryType, typename UniquingSetInfo, typename PredT>
-static void eraseFoldingSetIf(llvm::UniquingSet<EntryType, UniquingSetInfo> &FS,
- PredT &&Pred) {
- SmallVector<EntryType *, 16> ToRemove;
- for (auto &N : FS)
- if (Pred(N))
- ToRemove.push_back(&N);
-
- for (auto *N : ToRemove)
- FS.erase(N);
-}
-
-template <typename EntryType, typename PredT>
-static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
- SmallVector<EntryType *, 16> ToRemove;
- for (auto &N : FS)
- if (Pred(N))
- ToRemove.push_back(&N);
-
- for (auto *N : ToRemove)
- FS.erase(N);
-}
-
-template <typename EntryType, typename PredT>
-static void
-eraseFoldingSetIf(llvm::ContextualFoldingSet<EntryType, ASTContext &> &FS,
- PredT &&Pred) {
- SmallVector<EntryType *, 16> ToRemove;
-
- for (auto &N : FS)
- if (Pred(N))
- ToRemove.push_back(&N);
-
- for (auto *N : ToRemove)
- FS.erase(N);
-}
-
-/// Erase from DenseMap based on predicate
-template <typename KeyT, typename ValueT, typename PredT>
-static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
- SmallVector<KeyT, 16> ToRemove;
- for (auto &KV : Map)
- if (Pred(KV))
- ToRemove.push_back(KV.getFirst());
-
- for (auto &Key : ToRemove)
- Map.erase(Key);
-}
-
-/// Erase from DenseMap based on predicate
-template <typename ValueT, typename PredT>
-static void eraseDenseSetIf(llvm::DenseSet<ValueT> &Set, PredT &&Pred) {
- SmallVector<ValueT, 16> ToRemove;
-
- for (auto &V : Set)
- if (Pred(V))
- ToRemove.push_back(V);
-
- for (auto &Key : ToRemove)
- Set.erase(Key);
-}
-
-void ASTContextStateRecovery::stash(StashCheckPoint &CP) {
- CP.TypesSize = Ctx.Types.size();
- CP.ExtQualNodesSize = Ctx.ExtQualNodes.size();
- CP.ComplexTypesSize = Ctx.ComplexTypes.size();
- CP.PointerTypesSize = Ctx.PointerTypes.size();
- CP.AdjustedTypesSize = Ctx.AdjustedTypes.size();
- CP.BlockPointerTypesSize = Ctx.BlockPointerTypes.size();
- CP.LValueReferenceTypesSize = Ctx.LValueReferenceTypes.size();
- CP.RValueReferenceTypesSize = Ctx.RValueReferenceTypes.size();
- CP.MemberPointerTypesSize = Ctx.MemberPointerTypes.size();
-
- CP.ConstantArrayTypesSize = Ctx.ConstantArrayTypes.size();
- CP.IncompleteArrayTypesSize = Ctx.IncompleteArrayTypes.size();
- CP.VariableArrayTypesSize = Ctx.VariableArrayTypes.size();
-
- CP.DependentSizedArrayTypesSize = Ctx.DependentSizedArrayTypes.size();
- CP.DependentSizedExtVectorTypesSize = Ctx.DependentSizedExtVectorTypes.size();
- CP.DependentAddressSpaceTypesSize = Ctx.DependentAddressSpaceTypes.size();
- CP.VectorTypesSize = Ctx.VectorTypes.size();
- CP.DependentVectorTypesSize = Ctx.DependentVectorTypes.size();
- CP.MatrixTypesSize = Ctx.MatrixTypes.size();
- CP.DependentSizedMatrixTypesSize = Ctx.DependentSizedMatrixTypes.size();
- CP.FunctionNoProtoTypesSize = Ctx.FunctionNoProtoTypes.size();
- CP.FunctionProtoTypesSize = Ctx.FunctionProtoTypes.size();
- CP.DependentTypeOfExprTypesSize = Ctx.DependentTypeOfExprTypes.size();
- CP.DependentDecltypeTypesSize = Ctx.DependentDecltypeTypes.size();
-
- CP.DependentPackIndexingTypesSize = Ctx.DependentPackIndexingTypes.size();
-
- CP.TemplateTypeParmTypesSize = Ctx.TemplateTypeParmTypes.size();
- CP.ObjCTypeParamTypesSize = Ctx.ObjCTypeParamTypes.size();
- CP.SubstTemplateTypeParmTypesSize = Ctx.SubstTemplateTypeParmTypes.size();
- CP.SubstTemplateTypeParmPackTypesSize =
- Ctx.SubstTemplateTypeParmPackTypes.size();
- CP.SubstBuiltinTemplatePackTypesSize =
- Ctx.SubstBuiltinTemplatePackTypes.size();
-
- CP.TemplateSpecializationTypesSize = Ctx.TemplateSpecializationTypes.size();
- CP.ParenTypesSize = Ctx.ParenTypes.size();
- CP.TagTypesSize = Ctx.TagTypes.size();
- CP.UnresolvedUsingTypesSize = Ctx.UnresolvedUsingTypes.size();
- CP.UsingTypesSize = Ctx.UsingTypes.size();
- CP.TypedefTypesSize = Ctx.TypedefTypes.size();
- CP.DependentNameTypesSize = Ctx.DependentNameTypes.size();
- CP.PackExpansionTypesSize = Ctx.PackExpansionTypes.size();
- CP.ObjCObjectTypesSize = Ctx.ObjCObjectTypes.size();
- CP.ObjCObjectPointerTypesSize = Ctx.ObjCObjectPointerTypes.size();
- CP.UnaryTransformTypesSize = Ctx.UnaryTransformTypes.size();
-
- CP.AutoTypesSize = Ctx.AutoTypes.size();
- CP.DeducedTemplateSpecializationTypesSize =
- Ctx.DeducedTemplateSpecializationTypes.size();
- CP.AtomicTypesSize = Ctx.AtomicTypes.size();
- CP.AttributedTypesSize = Ctx.AttributedTypes.size();
- CP.PipeTypesSize = Ctx.PipeTypes.size();
- CP.BitIntTypesSize = Ctx.BitIntTypes.size();
- CP.DependentBitIntTypesSize = Ctx.DependentBitIntTypes.size();
- CP.BTFTagAttributedTypesSize = Ctx.BTFTagAttributedTypes.size();
- CP.HLSLAttributedResourceTypesSize = Ctx.HLSLAttributedResourceTypes.size();
- CP.HLSLInlineSpirvTypesSize = Ctx.HLSLInlineSpirvTypes.size();
-
- CP.CountAttributedTypesSize = Ctx.CountAttributedTypes.size();
-
- CP.QualifiedTemplateNamesSize = Ctx.QualifiedTemplateNames.size();
- CP.DependentTemplateNamesSize = Ctx.DependentTemplateNames.size();
- CP.SubstTemplateTemplateParmsSize = Ctx.SubstTemplateTemplateParms.size();
- CP.SubstTemplateTemplateParmPacksSize =
- Ctx.SubstTemplateTemplateParmPacks.size();
- CP.DeducedTemplatesSize = Ctx.DeducedTemplates.size();
-
- CP.ArrayParameterTypesSize = Ctx.ArrayParameterTypes.size();
-
- CP.PredefinedSugarTypesSize = Ctx.PredefinedSugarTypes.size();
-
- CP.NamespaceAndPrefixStoragesSize = Ctx.NamespaceAndPrefixStorages.size();
-
- CP.ASTRecordLayoutsSize = Ctx.ASTRecordLayouts.size();
-
- CP.MemoizedTypeInfoSize = Ctx.MemoizedTypeInfo.size();
-
- CP.MemoizedUnadjustedAlignSize = Ctx.MemoizedUnadjustedAlign.size();
-
- CP.KeyFunctionsSize = Ctx.KeyFunctions.size();
-
- CP.BlockVarCopyInitsSize = Ctx.BlockVarCopyInits.size();
-
- CP.MSGuidDeclsSize = Ctx.MSGuidDecls.size();
-
- CP.UnnamedGlobalConstantDeclsSize = Ctx.UnnamedGlobalConstantDecls.size();
-
- CP.TemplateParamObjectDeclsSize = Ctx.TemplateParamObjectDecls.size();
-
- CP.StringLiteralCacheSize = Ctx.StringLiteralCache.size();
-
- CP.DestroyingOperatorDeletesSize = Ctx.DestroyingOperatorDeletes.size();
- CP.TypeAwareOperatorNewAndDeletesSize =
- Ctx.TypeAwareOperatorNewAndDeletes.size();
-
- CP.OperatorDeletesForVirtualDtorSize =
- Ctx.OperatorDeletesForVirtualDtor.size();
-
- CP.GlobalOperatorDeletesForVirtualDtorSize =
- Ctx.GlobalOperatorDeletesForVirtualDtor.size();
-
- CP.ArrayOperatorDeletesForVirtualDtorSize =
- Ctx.ArrayOperatorDeletesForVirtualDtor.size();
- CP.GlobalArrayOperatorDeletesForVirtualDtorSize =
- Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size();
-
- CP.MaybeRequireVectorDeletingDtorSize =
- Ctx.MaybeRequireVectorDeletingDtor.size();
-
- CP.MergedDeclsSize = Ctx.MergedDecls.size();
- CP.DeclAttrsSize = Ctx.DeclAttrs.size();
- CP.MergedDefModulesSize = Ctx.MergedDefModules.size();
-
- CP.ModuleInitializersSize = Ctx.ModuleInitializers.size();
- CP.PrimaryModuleNameMapSize = Ctx.PrimaryModuleNameMap.size();
- CP.SameModuleLookupSetSize = Ctx.SameModuleLookupSet.size();
-
- CP.ScalableVecTyMapSize = Ctx.ScalableVecTyMap.size();
- CP.LambdaCastPathsSize = Ctx.LambdaCastPaths.size();
- CP.RawCommentsSize = Ctx.RawComments.size();
- CP.RedeclChainCommentsSize = Ctx.RedeclChainComments.size();
- CP.CommentlessRedeclChainsSize = Ctx.CommentlessRedeclChains.size();
- CP.ParsedCommentsSize = Ctx.ParsedComments.size();
- CP.RelocatableClassesSize = Ctx.RelocatableClasses.size();
- CP.ParamIndicesSize = Ctx.ParamIndices.size();
- CP.MangleNumbersSize = Ctx.MangleNumbers.size();
- CP.StaticLocalNumbersSize = Ctx.StaticLocalNumbers.size();
- CP.TemplateOrInstantiationSize = Ctx.TemplateOrInstantiation.size();
-
- CP.InstantiatedFromUsingDeclSize = Ctx.InstantiatedFromUsingDecl.size();
- CP.InstantiatedFromUsingEnumDeclSize =
- Ctx.InstantiatedFromUsingEnumDecl.size();
-
- CP.InstantiatedFromUsingShadowDeclSize =
- Ctx.InstantiatedFromUsingShadowDecl.size();
-
- CP.InstantiatedFromUnnamedFieldDeclSize =
- Ctx.InstantiatedFromUnnamedFieldDecl.size();
- CP.OverriddenMethodsSize = Ctx.OverriddenMethods.size();
- CP.MangleNumberingContextsSize = Ctx.MangleNumberingContexts.size();
- CP.ExtraMangleNumberingContextsSize = Ctx.ExtraMangleNumberingContexts.size();
- CP.TraversalScopeSize = Ctx.TraversalScope.size();
- // CP.LastSDM = Ctx.LastSDM;
- CP.AutoDeductTy = Ctx.AutoDeductTy; // Deduction against 'auto'.
- CP.AutoRRefDeductTy = Ctx.AutoRRefDeductTy; // Deduction against 'auto &&'.
-
- /// ObjectC
- CP.ObjCObjectTypesSize = Ctx.ObjCObjectTypes.size();
- CP.ObjCObjectPointerTypesSize = Ctx.ObjCObjectPointerTypes.size();
- CP.ObjCIdDeclCP = Ctx.ObjCIdDecl;
-
- CP.ObjCSelDeclCP = Ctx.ObjCSelDecl;
-
- CP.ObjCClassDeclCP = Ctx.ObjCClassDecl;
-
- CP.ObjCProtocolClassDeclCP = Ctx.ObjCProtocolClassDecl;
-
- /// DeclarationNameTable
- CP.DeclarationNames.CXXConstructorNamesSize =
- Ctx.DeclarationNames.CXXConstructorNames.size();
- CP.DeclarationNames.CXXDestructorNamesSize =
- Ctx.DeclarationNames.CXXDestructorNames.size();
- CP.DeclarationNames.CXXConversionFunctionNamesSize =
- Ctx.DeclarationNames.CXXConversionFunctionNames.size();
- for (unsigned I = 0; I < NUM_OVERLOADED_OPERATORS; I++)
- CP.DeclarationNames.CXXOperatorNamesCP[I] =
- Ctx.DeclarationNames.CXXOperatorNames[I];
- CP.DeclarationNames.CXXLiteralOperatorNamesSize =
- Ctx.DeclarationNames.CXXLiteralOperatorNames.size();
- CP.DeclarationNames.CXXDeductionGuideNamesSize =
- Ctx.DeclarationNames.CXXDeductionGuideNames.size();
-}
-
-class DeclContextRepairer {
- ASTContext &Ctx;
- llvm::SlabCheckPoint SlabCP;
- // in case to avoid revisit.
- llvm::SmallPtrSet<const DeclContext *, 8> Visited;
-
- bool isAfterCP(const void *P) const {
- return Ctx.getAllocator().isAfterCheckpoint(P, SlabCP);
- }
-
- static bool isExtensibleContainer(const Decl *D) {
- return isa<NamespaceDecl>(D) || isa<TagDecl>(D);
- }
-
- static bool isRedeclarableOrOnlyDecl(Decl *D) {
- return D->getPreviousDecl() != nullptr;
- }
-
- // Walk D's redecl chain looking for the newest decl that was from previous
- // PTU. Returns nullptr if the entire chain was created this PTU.
- template <typename DeclT> DeclT *findSurvivor(DeclT *D) {
- for (DeclT *It = D->getMostRecentDecl(); It; It = It->getPreviousDecl()) {
- if (!isAfterCP(It))
- return It;
- if (It == It->getFirstDecl())
- break;
- }
- return nullptr;
- }
-
- template <typename decl_type>
- void patchRedeclLink(Redeclarable<decl_type> *D, decl_type *Survivor) {
- D->getFirstDecl()->RedeclLink.setLatest(Survivor);
- }
-
- void repairLexicalChain(DeclContext &DC) {
- Decl *Prev = nullptr;
- for (Decl *Cur = DC.FirstDecl; Cur; Cur = Cur->getNextDeclInContext()) {
- if (isAfterCP(Cur)) {
- if (Prev) {
- DC.LastDecl = Prev;
- Prev->NextInContextAndBits.setPointer(nullptr);
- } else {
- DC.FirstDecl = DC.LastDecl = nullptr; // nothing survives
- }
- return;
- }
- Prev = Cur;
- }
- }
-
- NamedDecl *tryRepairRedeclChain(Decl *D) {
- if (!isRedeclarableOrOnlyDecl(D))
- return nullptr; // nothing to do
-
- NamedDecl *Survivor = nullptr;
-
- auto repairRedeclChain = [&](auto *RD) {
- // using DeclT = std::remove_pointer_t<decltype(RD)>;
-
- auto *SurvivorDecl = findSurvivor(RD);
- if (SurvivorDecl)
- patchRedeclLink(RD, SurvivorDecl);
-
- Survivor = SurvivorDecl;
- };
-
- switch (D->getKind()) {
- case Decl::Function:
- repairRedeclChain(cast<FunctionDecl>(D));
- break;
-
- case Decl::Var:
- repairRedeclChain(cast<VarDecl>(D));
- break;
-
- case Decl::Enum:
- repairRedeclChain(cast<TagDecl>(D));
- break;
-
- case Decl::Record:
- case Decl::CXXRecord:
- repairRedeclChain(cast<TagDecl>(D));
- break;
-
- case Decl::ClassTemplate:
- repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
- break;
-
- case Decl::FunctionTemplate:
- repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
- break;
-
- case Decl::TypeAliasTemplate:
- repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
- break;
-
- case Decl::VarTemplate:
- repairRedeclChain(cast<RedeclarableTemplateDecl>(D));
- break;
-
- case Decl::Namespace:
- repairRedeclChain(cast<NamespaceDecl>(D));
- break;
-
- default:
- break;
- }
-
- return Survivor;
- }
-
- void repairLookupEntry(StoredDeclsMap &Map, DeclarationName Key,
- StoredDeclsList &List, bool Recurse) {
- // Snapshot first: remove()/addOrReplaceDecl() below invalidate the live
- // view getLookupResult() returns.
- SmallVector<NamedDecl *, 4> Snapshot(List.getLookupResult().begin(),
- List.getLookupResult().end());
-
- for (NamedDecl *D : Snapshot) {
- if (Recurse && isExtensibleContainer(D))
- repairDeclContextLookup(cast<DeclContext>(D), Recurse);
-
- if (!isAfterCP(D))
- continue; // predates this PTU -- untouched
-
- NamedDecl *Survivor = tryRepairRedeclChain(D); // may be null
-
- if (!Survivor) {
- List.remove(D); // whole chain born this PTU
- continue;
- }
- assert(Survivor != D && "survivor postdates the checkpoint");
-
- // If the survivor is already visible under this name, D was an extra
- // entry (an overload); otherwise D had REPLACED the survivor's slot
- // via HandleRedeclaration and has to hand it back.
- if (llvm::is_contained(Snapshot, Survivor))
- List.remove(D);
- else
- List.addOrReplaceDecl(Survivor);
- }
-
- if (List.isNull())
- Map.erase(Key);
- }
-
-public:
- DeclContextRepairer(ASTContext &Ctx, llvm::SlabCheckPoint CP)
- : Ctx(Ctx), SlabCP(CP) {}
-
- template <typename decl_type>
- void repairRedeclchain(Redeclarable<decl_type> *D) {
- decl_type *SurviourDecl = findSurvivor(D);
- if (SurviourDecl)
- patchRedeclLink(D, SurviourDecl);
- }
-
- // Public entry point -- recursivly repair affected DCs.
- void repairDeclContextLookup(DeclContext *DC, bool NoRecursive = false) {
- DeclContext *Primary = DC->getPrimaryContext();
- if (!Visited.insert(Primary).second)
- return;
-
- bool CompletelyNew = isAfterCP(cast<Decl>(Primary)->getCanonicalDecl());
- StoredDeclsMap *Map = Primary->getLookupPtr();
-
- if (CompletelyNew) {
- // Primary's very first declaration happened this PTU -- the entire
- // entity is being discarded as a unit, so there's nothing to
- // individually repair.
- if (Map)
- Map->clear();
- return;
- }
-
- if (Map)
- for (auto &Entry : *Map)
- repairLookupEntry(*Map, Entry.first, Entry.second, NoRecursive);
- repairLexicalChain(*Primary);
- }
-};
-
-void ASTContextStateRecovery::restore(StashCheckPoint &CP,
- llvm::SlabCheckPoint SlabCP) {
- if (CP.AutoDeductTy != Ctx.AutoDeductTy) {
- llvm::dbgs() << "Ctx.AutoDeductTy != CP.AutoDeductTy\n";
- Ctx.AutoDeductTy = CP.AutoDeductTy;
- }
-
- if (CP.AutoRRefDeductTy != Ctx.AutoRRefDeductTy) {
- llvm::dbgs() << "Ctx.AutoRRefDeductTy != CP.AutoRRefDeductTy\n";
- Ctx.AutoRRefDeductTy = CP.AutoRRefDeductTy;
- }
-
- if (Ctx.TemplateTypeParmTypes.size() != CP.TemplateTypeParmTypesSize) {
- llvm::dbgs() << "Ctx.TemplateTypeParmTypes.size() != "
- "CP.TemplateTypeParmTypesSize\n";
- eraseFoldingSetIf(Ctx.TemplateTypeParmTypes,
- [&](TemplateTypeParmType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.TemplateTypeParmTypes.size() == CP.TemplateTypeParmTypesSize);
- }
-
- if (Ctx.TagTypes.size() != CP.TagTypesSize) {
- llvm::dbgs() << "Ctx.TagTypes.size() != CP.TagTypesSize\n";
- eraseFoldingSetIf(Ctx.TagTypes,
- [&](TagTypeFoldingSetPlaceholder &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
-
- assert(Ctx.TagTypes.size() == CP.TagTypesSize);
- }
-
- if (Ctx.ExtQualNodes.size() != CP.ExtQualNodesSize) {
- llvm::dbgs() << "Ctx.ExtQualNodes.size() != CP.ExtQualNodesSize\n";
- eraseFoldingSetIf(Ctx.ExtQualNodes, [&](ExtQuals &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.ExtQualNodes.size() == CP.ExtQualNodesSize);
- }
-
- if (Ctx.ComplexTypes.size() != CP.ComplexTypesSize) {
- llvm::dbgs() << "Ctx.ComplexTypes.size() != CP.ComplexTypesSize\n";
- eraseFoldingSetIf(Ctx.ComplexTypes, [&](ComplexType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.ComplexTypes.size() == CP.ComplexTypesSize);
- }
-
- if (Ctx.PointerTypes.size() != CP.PointerTypesSize) {
- llvm::dbgs() << "Ctx.PointerTypes.size() != CP.PointerTypesSize\n";
- eraseFoldingSetIf(Ctx.PointerTypes, [&](PointerType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.PointerTypes.size() == CP.PointerTypesSize);
- }
-
- if (Ctx.AdjustedTypes.size() != CP.AdjustedTypesSize) {
- llvm::dbgs() << "Ctx.AdjustedTypes.size() != CP.AdjustedTypesSize\n";
- eraseFoldingSetIf(Ctx.AdjustedTypes, [&](AdjustedType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.AdjustedTypes.size() == CP.AdjustedTypesSize);
- }
-
- if (Ctx.BlockPointerTypes.size() != CP.BlockPointerTypesSize) {
- llvm::dbgs()
- << "Ctx.BlockPointerTypes.size() != CP.BlockPointerTypesSize\n";
- eraseFoldingSetIf(Ctx.BlockPointerTypes,
- [&](BlockPointerType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.BlockPointerTypes.size() == CP.BlockPointerTypesSize);
- }
-
- if (Ctx.LValueReferenceTypes.size() != CP.LValueReferenceTypesSize) {
- llvm::dbgs()
- << "Ctx.LValueReferenceTypes.size() != CP.LValueReferenceTypesSize\n";
- eraseFoldingSetIf(Ctx.LValueReferenceTypes,
- [&](LValueReferenceType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.LValueReferenceTypes.size() == CP.LValueReferenceTypesSize);
- }
-
- if (Ctx.RValueReferenceTypes.size() != CP.RValueReferenceTypesSize) {
- llvm::dbgs()
- << "Ctx.RValueReferenceTypes.size() != CP.RValueReferenceTypesSize\n";
- eraseFoldingSetIf(Ctx.RValueReferenceTypes,
- [&](RValueReferenceType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.RValueReferenceTypes.size() == CP.RValueReferenceTypesSize);
- }
-
- if (Ctx.MemberPointerTypes.size() != CP.MemberPointerTypesSize) {
- llvm::dbgs()
- << "Ctx.MemberPointerTypes.size() != CP.MemberPointerTypesSize\n";
- eraseFoldingSetIf(Ctx.MemberPointerTypes,
- [&](MemberPointerType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.MemberPointerTypes.size() == CP.MemberPointerTypesSize);
- }
-
- if (Ctx.ConstantArrayTypes.size() != CP.ConstantArrayTypesSize) {
- llvm::dbgs()
- << "Ctx.ConstantArrayTypes.size() != CP.ConstantArrayTypesSize\n";
- eraseFoldingSetIf(Ctx.ConstantArrayTypes,
- [&](ConstantArrayType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.ConstantArrayTypes.size() == CP.ConstantArrayTypesSize);
- }
-
- if (Ctx.IncompleteArrayTypes.size() != CP.IncompleteArrayTypesSize) {
- llvm::dbgs()
- << "Ctx.IncompleteArrayTypes.size() != CP.IncompleteArrayTypesSize\n";
- eraseFoldingSetIf(Ctx.IncompleteArrayTypes,
- [&](IncompleteArrayType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.IncompleteArrayTypes.size() == CP.IncompleteArrayTypesSize);
- }
-
- // mutable std::vector<VariableArrayType*> VariableArrayTypes;
- if (Ctx.VariableArrayTypes.size() != CP.VariableArrayTypesSize) {
- llvm::dbgs()
- << "Ctx.VariableArrayTypes.size() != CP.VariableArrayTypesSize\n";
- Ctx.VariableArrayTypes.resize(CP.VariableArrayTypesSize);
- assert(Ctx.VariableArrayTypes.size() == CP.VariableArrayTypesSize);
- }
-
- if (Ctx.DependentSizedArrayTypes.size() != CP.DependentSizedArrayTypesSize) {
- llvm::dbgs() << "Ctx.DependentSizedArrayTypes.size() != "
- "CP.DependentSizedArrayTypesSize\n";
- eraseFoldingSetIf(Ctx.DependentSizedArrayTypes,
- [&](DependentSizedArrayType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentSizedArrayTypes.size() ==
- CP.DependentSizedArrayTypesSize);
- }
-
- if (Ctx.DependentSizedExtVectorTypes.size() !=
- CP.DependentSizedExtVectorTypesSize) {
- llvm::dbgs() << "Ctx.DependentSizedExtVectorTypes.size() != "
- "CP.DependentSizedExtVectorTypesSize\n";
- eraseFoldingSetIf(Ctx.DependentSizedExtVectorTypes,
- [&](DependentSizedExtVectorType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentSizedExtVectorTypes.size() ==
- CP.DependentSizedExtVectorTypesSize);
- }
-
- if (Ctx.DependentAddressSpaceTypes.size() !=
- CP.DependentAddressSpaceTypesSize) {
- llvm::dbgs() << "Ctx.DependentAddressSpaceTypes.size() != "
- "CP.DependentAddressSpaceTypesSize\n";
-
- eraseFoldingSetIf(Ctx.DependentAddressSpaceTypes,
- [&](DependentAddressSpaceType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentAddressSpaceTypes.size() ==
- CP.DependentAddressSpaceTypesSize);
- }
-
- if (Ctx.VectorTypes.size() != CP.VectorTypesSize) {
- llvm::dbgs() << "Ctx.VectorTypes.size() != CP.VectorTypesSize\n";
- eraseFoldingSetIf(Ctx.VectorTypes, [&](VectorType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.VectorTypes.size() == CP.VectorTypesSize);
- }
-
- if (Ctx.DependentVectorTypes.size() != CP.DependentVectorTypesSize) {
- llvm::dbgs()
- << "Ctx.DependentVectorTypes.size() != CP.DependentVectorTypesSize\n";
- eraseFoldingSetIf(Ctx.DependentVectorTypes,
- [&](DependentVectorType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentVectorTypes.size() == CP.DependentVectorTypesSize);
- }
-
- if (Ctx.MatrixTypes.size() != CP.MatrixTypesSize) {
- llvm::dbgs() << "Ctx.MatrixTypes.size() != CP.MatrixTypesSize\n";
- eraseFoldingSetIf(Ctx.MatrixTypes, [&](ConstantMatrixType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.MatrixTypes.size() == CP.MatrixTypesSize);
- }
-
- if (Ctx.DependentSizedMatrixTypes.size() !=
- CP.DependentSizedMatrixTypesSize) {
- llvm::dbgs() << "Ctx.DependentSizedMatrixTypes.size() != "
- "CP.DependentSizedMatrixTypesSize\n";
- eraseFoldingSetIf(Ctx.DependentSizedMatrixTypes,
- [&](DependentSizedMatrixType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentSizedMatrixTypes.size() ==
- CP.DependentSizedMatrixTypesSize);
- }
-
- if (Ctx.FunctionNoProtoTypes.size() != CP.FunctionNoProtoTypesSize) {
- llvm::dbgs()
- << "Ctx.FunctionNoProtoTypes.size() != CP.FunctionNoProtoTypesSize\n";
- eraseFoldingSetIf(Ctx.FunctionNoProtoTypes,
- [&](FunctionNoProtoType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.FunctionNoProtoTypes.size() == CP.FunctionNoProtoTypesSize);
- }
-
- if (Ctx.FunctionProtoTypes.size() != CP.FunctionProtoTypesSize) {
- llvm::dbgs()
- << "Ctx.FunctionProtoTypes.size() != CP.FunctionProtoTypesSize\n";
- eraseFoldingSetIf(Ctx.FunctionProtoTypes,
- [&](FunctionProtoType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.FunctionProtoTypes.size() == CP.FunctionProtoTypesSize);
- }
-
- if (Ctx.DependentTypeOfExprTypes.size() != CP.DependentTypeOfExprTypesSize) {
- llvm::dbgs() << "Ctx.DependentTypeOfExprTypes.size() != "
- "CP.DependentTypeOfExprTypesSize\n";
- eraseFoldingSetIf(Ctx.DependentTypeOfExprTypes,
- [&](DependentTypeOfExprType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentTypeOfExprTypes.size() ==
- CP.DependentTypeOfExprTypesSize);
- }
-
- if (Ctx.DependentDecltypeTypes.size() != CP.DependentDecltypeTypesSize) {
- llvm::dbgs() << "Ctx.DependentDecltypeTypes.size() != "
- "CP.DependentDecltypeTypesSize\n";
- eraseFoldingSetIf(Ctx.DependentDecltypeTypes,
- [&](DependentDecltypeType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentDecltypeTypes.size() == CP.DependentDecltypeTypesSize);
- }
-
- if (Ctx.DependentPackIndexingTypes.size() !=
- CP.DependentPackIndexingTypesSize) {
- llvm::dbgs() << "Ctx.DependentPackIndexingTypes.size() != "
- "CP.DependentPackIndexingTypesSize\n";
- eraseFoldingSetIf(Ctx.DependentPackIndexingTypes,
- [&](PackIndexingType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentPackIndexingTypes.size() ==
- CP.DependentPackIndexingTypesSize);
- }
-
- if (Ctx.TemplateTypeParmTypes.size() != CP.TemplateTypeParmTypesSize) {
- llvm::dbgs() << "Ctx.TemplateTypeParmTypes.size() != "
- "CP.TemplateTypeParmTypesSize\n";
- eraseFoldingSetIf(Ctx.TemplateTypeParmTypes,
- [&](TemplateTypeParmType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.TemplateTypeParmTypes.size() == CP.TemplateTypeParmTypesSize);
- }
-
- // mutable llvm::FoldingSet<ObjCTypeParamType> ObjCTypeParamTypes;
-
- if (Ctx.SubstTemplateTypeParmTypes.size() !=
- CP.SubstTemplateTypeParmTypesSize) {
- llvm::dbgs() << "Ctx.SubstTemplateTypeParmTypes.size() != "
- "CP.SubstTemplateTypeParmTypesSize\n";
- eraseFoldingSetIf(Ctx.SubstTemplateTypeParmTypes,
- [&](SubstTemplateTypeParmType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.SubstTemplateTypeParmTypes.size() ==
- CP.SubstTemplateTypeParmTypesSize);
- }
-
- if (Ctx.SubstTemplateTypeParmPackTypes.size() !=
- CP.SubstTemplateTypeParmPackTypesSize) {
- llvm::dbgs() << "Ctx.SubstTemplateTypeParmPackTypes.size() != "
- "CP.SubstTemplateTypeParmPackTypesSize\n";
- eraseFoldingSetIf(Ctx.SubstTemplateTypeParmPackTypes,
- [&](SubstTemplateTypeParmPackType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.SubstTemplateTypeParmPackTypes.size() ==
- CP.SubstTemplateTypeParmPackTypesSize);
- }
-
- if (Ctx.SubstBuiltinTemplatePackTypes.size() !=
- CP.SubstBuiltinTemplatePackTypesSize) {
- llvm::dbgs() << "Ctx.SubstBuiltinTemplatePackTypes.size() != "
- "CP.SubstBuiltinTemplatePackTypesSize\n";
- eraseFoldingSetIf(Ctx.SubstBuiltinTemplatePackTypes,
- [&](SubstBuiltinTemplatePackType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.SubstBuiltinTemplatePackTypes.size() ==
- CP.SubstBuiltinTemplatePackTypesSize);
- }
-
- if (Ctx.TemplateSpecializationTypes.size() !=
- CP.TemplateSpecializationTypesSize) {
- llvm::dbgs() << "Ctx.TemplateSpecializationTypes.size() != "
- "CP.TemplateSpecializationTypesSize\n";
- eraseFoldingSetIf(Ctx.TemplateSpecializationTypes,
- [&](TemplateSpecializationType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.TemplateSpecializationTypes.size() ==
- CP.TemplateSpecializationTypesSize);
- }
-
- if (Ctx.ParenTypes.size() != CP.ParenTypesSize) {
- llvm::dbgs() << "Ctx.ParenTypes.size() != CP.ParenTypesSize\n";
- eraseFoldingSetIf(Ctx.ParenTypes, [&](ParenType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.ParenTypes.size() == CP.ParenTypesSize);
- }
-
- if (Ctx.TagTypes.size() != CP.TagTypesSize) {
- llvm::dbgs() << "Ctx.TagTypes.size() != CP.TagTypesSize\n";
- eraseFoldingSetIf(Ctx.TagTypes,
- [&](TagTypeFoldingSetPlaceholder &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.TagTypes.size() == CP.TagTypesSize);
- }
-
- if (Ctx.UnresolvedUsingTypes.size() != CP.UnresolvedUsingTypesSize) {
- llvm::dbgs()
- << "Ctx.UnresolvedUsingTypes.size() != CP.UnresolvedUsingTypesSize\n";
- eraseFoldingSetIf(
- Ctx.UnresolvedUsingTypes,
- [&](FoldingSetPlaceholder<UnresolvedUsingType> &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.UnresolvedUsingTypes.size() == CP.UnresolvedUsingTypesSize);
- }
-
- if (Ctx.UsingTypes.size() != CP.UsingTypesSize) {
- llvm::dbgs() << "Ctx.UsingTypes.size() != CP.UsingTypesSize\n";
- eraseFoldingSetIf(Ctx.UsingTypes, [&](UsingType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.UsingTypes.size() == CP.UsingTypesSize);
- }
-
- if (Ctx.TypedefTypes.size() != CP.TypedefTypesSize) {
- llvm::dbgs() << "Ctx.TypedefTypes.size() != CP.TypedefTypesSize\n";
- eraseFoldingSetIf(Ctx.TypedefTypes,
- [&](FoldingSetPlaceholder<TypedefType> &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.TypedefTypes.size() == CP.TypedefTypesSize);
- }
-
- if (Ctx.DependentNameTypes.size() != CP.DependentNameTypesSize) {
- llvm::dbgs()
- << "Ctx.DependentNameTypes.size() != CP.DependentNameTypesSize\n";
- eraseFoldingSetIf(Ctx.DependentNameTypes,
- [&](DependentNameType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentNameTypes.size() == CP.DependentNameTypesSize);
- }
-
- if (Ctx.PackExpansionTypes.size() != CP.PackExpansionTypesSize) {
- llvm::dbgs()
- << "Ctx.PackExpansionTypes.size() != CP.PackExpansionTypesSize\n";
- eraseFoldingSetIf(Ctx.PackExpansionTypes,
- [&](PackExpansionType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.PackExpansionTypes.size() == CP.PackExpansionTypesSize);
- }
-
- // mutable llvm::FoldingSet<ObjCObjectTypeImpl> ObjCObjectTypes;
- // mutable llvm::FoldingSet<ObjCObjectPointerType> ObjCObjectPointerTypes;
-
- if (Ctx.UnaryTransformTypes.size() != CP.UnaryTransformTypesSize) {
- llvm::dbgs()
- << "Ctx.UnaryTransformTypes.size() != CP.UnaryTransformTypesSize\n";
- eraseFoldingSetIf(Ctx.UnaryTransformTypes,
- [&](UnaryTransformType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.UnaryTransformTypes.size() == CP.UnaryTransformTypesSize);
- }
-
- if (Ctx.AutoTypes.size() != CP.AutoTypesSize) {
- llvm::dbgs() << "Ctx.AutoTypes.size() != CP.AutoTypesSize\n";
- // mutable llvm::DenseMap<llvm::FoldingSetNodeID, AutoType *> AutoTypes;
- eraseDenseMapIf(
- Ctx.AutoTypes,
- [&](llvm::detail::DenseMapPair<llvm::FoldingSetNodeIDRef, AutoType *>
- &KV) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(KV.getSecond()), SlabCP);
- });
- assert(Ctx.AutoTypes.size() == CP.AutoTypesSize);
- }
-
- if (Ctx.DeducedTemplateSpecializationTypes.size() !=
- CP.DeducedTemplateSpecializationTypesSize) {
- llvm::dbgs() << "Ctx.DeducedTemplateSpecializationTypes.size() != "
- "CP.DeducedTemplateSpecializationTypesSize\n";
- eraseFoldingSetIf(Ctx.DeducedTemplateSpecializationTypes,
- [&](DeducedTemplateSpecializationType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DeducedTemplateSpecializationTypes.size() ==
- CP.DeducedTemplateSpecializationTypesSize);
- }
-
- if (Ctx.AtomicTypes.size() != CP.AtomicTypesSize) {
- llvm::dbgs() << "Ctx.AtomicTypes.size() != CP.AtomicTypesSize\n";
- eraseFoldingSetIf(Ctx.AtomicTypes, [&](AtomicType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.AtomicTypes.size() == CP.AtomicTypesSize);
- }
-
- if (Ctx.AttributedTypes.size() != CP.AttributedTypesSize) {
- llvm::dbgs() << "Ctx.AttributedTypes.size() != CP.AttributedTypesSize\n";
- eraseFoldingSetIf(Ctx.AttributedTypes, [&](AttributedType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.AttributedTypes.size() == CP.AttributedTypesSize);
- }
-
- if (Ctx.PipeTypes.size() != CP.PipeTypesSize) {
- llvm::dbgs() << "Ctx.PipeTypes.size() != CP.PipeTypesSize\n";
- eraseFoldingSetIf(Ctx.PipeTypes, [&](PipeType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.PipeTypes.size() == CP.PipeTypesSize);
- }
-
- if (Ctx.BitIntTypes.size() != CP.BitIntTypesSize) {
- llvm::dbgs() << "Ctx.BitIntTypes.size() != CP.BitIntTypesSize\n";
- eraseFoldingSetIf(Ctx.BitIntTypes, [&](BitIntType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(Ctx.BitIntTypes.size() == CP.BitIntTypesSize);
- }
-
- if (Ctx.DependentBitIntTypes.size() != CP.DependentBitIntTypesSize) {
- llvm::dbgs()
- << "Ctx.DependentBitIntTypes.size() != CP.DependentBitIntTypesSize\n";
- eraseFoldingSetIf(Ctx.DependentBitIntTypes,
- [&](DependentBitIntType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentBitIntTypes.size() == CP.DependentBitIntTypesSize);
- }
-
- if (Ctx.BTFTagAttributedTypes.size() != CP.BTFTagAttributedTypesSize) {
- llvm::dbgs() << "Ctx.BTFTagAttributedTypes.size() != "
- "CP.BTFTagAttributedTypesSize\n";
- eraseFoldingSetIf(Ctx.BTFTagAttributedTypes,
- [&](BTFTagAttributedType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.BTFTagAttributedTypes.size() == CP.BTFTagAttributedTypesSize);
- }
-
- if (Ctx.HLSLAttributedResourceTypes.size() !=
- CP.HLSLAttributedResourceTypesSize) {
- llvm::dbgs() << "Ctx.HLSLAttributedResourceTypes.size() != "
- "CP.HLSLAttributedResourceTypesSize\n";
- eraseFoldingSetIf(Ctx.HLSLAttributedResourceTypes,
- [&](HLSLAttributedResourceType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.HLSLAttributedResourceTypes.size() ==
- CP.HLSLAttributedResourceTypesSize);
- }
-
- if (Ctx.HLSLInlineSpirvTypes.size() != CP.HLSLInlineSpirvTypesSize) {
- llvm::dbgs()
- << "Ctx.HLSLInlineSpirvTypes.size() != CP.HLSLInlineSpirvTypesSize\n";
- eraseFoldingSetIf(Ctx.HLSLInlineSpirvTypes,
- [&](HLSLInlineSpirvType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.HLSLInlineSpirvTypes.size() == CP.HLSLInlineSpirvTypesSize);
- }
-
- if (Ctx.CountAttributedTypes.size() != CP.CountAttributedTypesSize) {
- llvm::dbgs()
- << "Ctx.CountAttributedTypes.size() != CP.CountAttributedTypesSize\n";
- eraseFoldingSetIf(Ctx.CountAttributedTypes,
- [&](CountAttributedType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.CountAttributedTypes.size() == CP.CountAttributedTypesSize);
- }
-
- if (Ctx.QualifiedTemplateNames.size() != CP.QualifiedTemplateNamesSize) {
- llvm::dbgs() << "Ctx.QualifiedTemplateNames.size() != "
- "CP.QualifiedTemplateNamesSize\n";
- eraseFoldingSetIf(Ctx.QualifiedTemplateNames,
- [&](QualifiedTemplateName &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.QualifiedTemplateNames.size() == CP.QualifiedTemplateNamesSize);
- }
-
- if (Ctx.DependentTemplateNames.size() != CP.DependentTemplateNamesSize) {
- llvm::dbgs() << "Ctx.DependentTemplateNames.size() != "
- "CP.DependentTemplateNamesSize\n";
- eraseFoldingSetIf(Ctx.DependentTemplateNames,
- [&](DependentTemplateName &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DependentTemplateNames.size() == CP.DependentTemplateNamesSize);
- }
-
- if (Ctx.SubstTemplateTemplateParms.size() !=
- CP.SubstTemplateTemplateParmsSize) {
- llvm::dbgs() << "Ctx.SubstTemplateTemplateParms.size() != "
- "CP.SubstTemplateTemplateParmsSize\n";
- eraseFoldingSetIf(Ctx.SubstTemplateTemplateParms,
- [&](SubstTemplateTemplateParmStorage &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.SubstTemplateTemplateParms.size() ==
- CP.SubstTemplateTemplateParmsSize);
- }
-
- if (Ctx.SubstTemplateTemplateParmPacks.size() !=
- CP.SubstTemplateTemplateParmPacksSize) {
- llvm::dbgs() << "Ctx.SubstTemplateTemplateParmPacks.size() != "
- "CP.SubstTemplateTemplateParmPacksSize\n";
- eraseFoldingSetIf(Ctx.SubstTemplateTemplateParmPacks,
- [&](SubstTemplateTemplateParmPackStorage &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.SubstTemplateTemplateParmPacks.size() ==
- CP.SubstTemplateTemplateParmPacksSize);
- }
-
- if (Ctx.DeducedTemplates.size() != CP.DeducedTemplatesSize) {
- llvm::dbgs() << "Ctx.DeducedTemplates.size() != CP.DeducedTemplatesSize\n";
- eraseFoldingSetIf(Ctx.DeducedTemplates,
- [&](DeducedTemplateStorage &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.DeducedTemplates.size() == CP.DeducedTemplatesSize);
- }
-
- if (Ctx.ArrayParameterTypes.size() != CP.ArrayParameterTypesSize) {
- llvm::dbgs()
- << "Ctx.ArrayParameterTypes.size() != CP.ArrayParameterTypesSize\n";
- eraseFoldingSetIf(Ctx.ArrayParameterTypes,
- [&](ArrayParameterType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(Ctx.ArrayParameterTypes.size() == CP.ArrayParameterTypesSize);
- }
-
- if (CP.PredefinedSugarTypesSize != Ctx.PredefinedSugarTypes.size()) {
- llvm::dbgs() << "if (CP.PredefinedSugarTypesSize != "
- "Ctx.PredefinedSugarTypes.size()\n";
- // mutable std::array<Type *,
- // llvm::to_underlying(PredefinedSugarType::Kind::Last) +
- // 1>
- // PredefinedSugarTypes{};
- assert(CP.PredefinedSugarTypesSize == Ctx.PredefinedSugarTypes.size());
- }
-
- if (CP.NamespaceAndPrefixStoragesSize !=
- Ctx.NamespaceAndPrefixStorages.size()) {
- llvm::dbgs() << "if (CP.NamespaceAndPrefixStoragesSize != "
- "Ctx.NamespaceAndPrefixStorages.size()\n";
- eraseFoldingSetIf(Ctx.NamespaceAndPrefixStorages,
- [&](NamespaceAndPrefixStorage &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(CP.NamespaceAndPrefixStoragesSize ==
- Ctx.NamespaceAndPrefixStorages.size());
- }
-
- if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()) {
- llvm::dbgs()
- << "if (CP.ASTRecordLayoutsSize != Ctx.ASTRecordLayouts.size()\n";
- std::vector<const ASTRecordLayout *> ToBeDestroyed;
- eraseDenseMapIf(
- Ctx.ASTRecordLayouts,
- [&](llvm::detail::DenseMapPair<const RecordDecl *,
- const ASTRecordLayout *> &KV) -> bool {
- bool IsAfter =
- Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(const_cast<RecordDecl *>(KV.getFirst())),
- SlabCP) ||
- Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(
- const_cast<ASTRecordLayout *>(KV.getSecond())),
- SlabCP);
- if (IsAfter)
- const_cast<ASTRecordLayout *>(KV.getSecond())->Destroy(Ctx);
- return IsAfter;
- });
- assert(CP.ASTRecordLayoutsSize == Ctx.ASTRecordLayouts.size());
- }
-
- if (CP.MemoizedTypeInfoSize != Ctx.MemoizedTypeInfo.size()) {
- llvm::dbgs()
- << "if (CP.MemoizedTypeInfoSize != Ctx.MemoizedTypeInfo.size() "
- << CP.MemoizedTypeInfoSize << " != " << Ctx.MemoizedTypeInfo.size()
- << " \n";
- eraseDenseMapIf(
- Ctx.MemoizedTypeInfo,
- [&](llvm::detail::DenseMapPair<const Type *, struct TypeInfo> &KV)
- -> bool {
- // llvm::outs() << "Type * In Range : [ "
- // << static_cast<void *>(SlabCP.CurPtr) << " < "
- // << KV.getFirst() << " < "
- // << static_cast<void *>(SlabCP.End) << "]\n";
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(const_cast<Type *>(KV.getFirst())), SlabCP);
- });
- // assert(CP.MemoizedTypeInfoSize == Ctx.MemoizedTypeInfo.size());
- }
-
- if (CP.MemoizedUnadjustedAlignSize != Ctx.MemoizedUnadjustedAlign.size()) {
- llvm::dbgs() << "if (CP.MemoizedUnadjustedAlignSize != "
- "Ctx.MemoizedUnadjustedAlign.size()\n";
- eraseDenseMapIf(
- Ctx.MemoizedUnadjustedAlign,
- [&](llvm::detail::DenseMapPair<const Type *, unsigned> &KV) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(const_cast<Type *>(KV.getFirst())), SlabCP);
- });
- assert(CP.MemoizedUnadjustedAlignSize ==
- Ctx.MemoizedUnadjustedAlign.size());
- }
-
- if (CP.KeyFunctionsSize != Ctx.KeyFunctions.size()) {
- llvm::dbgs() << "if (CP.KeyFunctionsSize != Ctx.KeyFunctions.size()\n";
- eraseDenseMapIf(
- Ctx.KeyFunctions,
- [&](llvm::detail::DenseMapPair<const CXXRecordDecl *, LazyDeclPtr> &KV)
- -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(const_cast<CXXRecordDecl *>(KV.getFirst())),
- SlabCP);
- });
- // assert(CP.KeyFunctionsSize == Ctx.KeyFunctions.size());
- }
-
- if (CP.BlockVarCopyInitsSize != Ctx.BlockVarCopyInits.size()) {
- llvm::dbgs()
- << "if (CP.BlockVarCopyInitsSize != Ctx.BlockVarCopyInits.size()\n";
- eraseDenseMapIf(
- Ctx.BlockVarCopyInits,
- [&](llvm::detail::DenseMapPair<const VarDecl *, BlockVarCopyInit> &KV)
- -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(const_cast<VarDecl *>(KV.getFirst())),
- SlabCP);
- });
- assert(CP.BlockVarCopyInitsSize == Ctx.BlockVarCopyInits.size());
- }
-
- if (CP.MSGuidDeclsSize != Ctx.MSGuidDecls.size()) {
- llvm::dbgs() << "if (CP.MSGuidDeclsSize != Ctx.MSGuidDecls.size()\n";
- eraseFoldingSetIf(Ctx.MSGuidDecls, [&](MSGuidDecl &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(&Node),
- SlabCP);
- });
- assert(CP.MSGuidDeclsSize == Ctx.MSGuidDecls.size());
- }
-
- if (CP.UnnamedGlobalConstantDeclsSize !=
- Ctx.UnnamedGlobalConstantDecls.size()) {
- llvm::dbgs() << "if (CP.UnnamedGlobalConstantDeclsSize != "
- "Ctx.UnnamedGlobalConstantDecls.size()\n";
- eraseFoldingSetIf(Ctx.UnnamedGlobalConstantDecls,
- [&](UnnamedGlobalConstantDecl &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(CP.UnnamedGlobalConstantDeclsSize ==
- Ctx.UnnamedGlobalConstantDecls.size());
- }
-
- if (CP.TemplateParamObjectDeclsSize != Ctx.TemplateParamObjectDecls.size()) {
- llvm::dbgs() << "if (CP.TemplateParamObjectDeclsSize != "
- "Ctx.TemplateParamObjectDecls.size()\n";
- eraseFoldingSetIf(Ctx.TemplateParamObjectDecls,
- [&](TemplateParamObjectDecl &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(CP.TemplateParamObjectDeclsSize ==
- Ctx.TemplateParamObjectDecls.size());
- }
-
- if (CP.StringLiteralCacheSize != Ctx.StringLiteralCache.size()) {
- llvm::dbgs()
- << "if (CP.StringLiteralCacheSize != Ctx.StringLiteralCache.size()\n";
- // mutable llvm::StringMap<StringLiteral *> StringLiteralCache;
- assert(CP.StringLiteralCacheSize == Ctx.StringLiteralCache.size());
- }
-
- if (CP.DestroyingOperatorDeletesSize !=
- Ctx.DestroyingOperatorDeletes.size()) {
- llvm::dbgs() << "if (CP.DestroyingOperatorDeletesSize != "
- "Ctx.DestroyingOperatorDeletes.size()\n";
- eraseDenseSetIf(
- Ctx.DestroyingOperatorDeletes, [&](const FunctionDecl *FD) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(const_cast<FunctionDecl *>(FD)), SlabCP);
- });
- assert(CP.DestroyingOperatorDeletesSize ==
- Ctx.DestroyingOperatorDeletes.size());
- }
-
- if (CP.TypeAwareOperatorNewAndDeletesSize !=
- Ctx.TypeAwareOperatorNewAndDeletes.size()) {
- llvm::dbgs() << "CP.TypeAwareOperatorNewAndDeletesSize != "
- "Ctx.TypeAwareOperatorNewAndDeletes.size()\n";
- eraseDenseSetIf(Ctx.TypeAwareOperatorNewAndDeletes,
- [&](const FunctionDecl *FD) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(const_cast<FunctionDecl *>(FD)),
- SlabCP);
- });
- assert(CP.TypeAwareOperatorNewAndDeletesSize ==
- Ctx.TypeAwareOperatorNewAndDeletes.size());
- }
-
- if (CP.OperatorDeletesForVirtualDtorSize !=
- Ctx.OperatorDeletesForVirtualDtor.size()) {
- llvm::dbgs() << "CP.OperatorDeletesForVirtualDtorSize != "
- "Ctx.OperatorDeletesForVirtualDtor.size()\n";
- eraseDenseMapIf(
- Ctx.OperatorDeletesForVirtualDtor,
- [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
- FunctionDecl *> &KV) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(
- const_cast<CXXDestructorDecl *>(KV.getFirst())),
- SlabCP) ||
- Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(KV.getSecond()), SlabCP);
- });
- // assert(CP.OperatorDeletesForVirtualDtorSize ==
- // Ctx.OperatorDeletesForVirtualDtor.size());
- }
-
- if (CP.GlobalOperatorDeletesForVirtualDtorSize !=
- Ctx.GlobalOperatorDeletesForVirtualDtor.size()) {
- llvm::dbgs() << "CP.GlobalOperatorDeletesForVirtualDtorSize != "
- "Ctx.GlobalOperatorDeletesForVirtualDtor.size()\n";
- eraseDenseMapIf(
- Ctx.GlobalOperatorDeletesForVirtualDtor,
- [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
- FunctionDecl *> &KV) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(
- const_cast<CXXDestructorDecl *>(KV.getFirst())),
- SlabCP) ||
- Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(KV.getSecond()), SlabCP);
- });
- assert(CP.GlobalOperatorDeletesForVirtualDtorSize ==
- Ctx.GlobalOperatorDeletesForVirtualDtor.size());
- }
-
- if (CP.ArrayOperatorDeletesForVirtualDtorSize !=
- Ctx.ArrayOperatorDeletesForVirtualDtor.size()) {
- llvm::dbgs() << "CP.ArrayOperatorDeletesForVirtualDtorSize != "
- "Ctx.ArrayOperatorDeletesForVirtualDtor.size()\n";
- eraseDenseMapIf(
- Ctx.ArrayOperatorDeletesForVirtualDtor,
- [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
- FunctionDecl *> &KV) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(
- const_cast<CXXDestructorDecl *>(KV.getFirst())),
- SlabCP) ||
- Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(KV.getSecond()), SlabCP);
- });
- assert(CP.ArrayOperatorDeletesForVirtualDtorSize ==
- Ctx.ArrayOperatorDeletesForVirtualDtor.size());
- }
-
- if (CP.GlobalArrayOperatorDeletesForVirtualDtorSize !=
- Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size()) {
- llvm::dbgs() << "CP.GlobalArrayOperatorDeletesForVirtualDtorSize != "
- "Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size()\n";
- eraseDenseMapIf(
- Ctx.GlobalArrayOperatorDeletesForVirtualDtor,
- [&](llvm::detail::DenseMapPair<const CXXDestructorDecl *,
- FunctionDecl *> &KV) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(
- const_cast<CXXDestructorDecl *>(KV.getFirst())),
- SlabCP) ||
- Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(KV.getSecond()), SlabCP);
- });
- assert(CP.GlobalArrayOperatorDeletesForVirtualDtorSize ==
- Ctx.GlobalArrayOperatorDeletesForVirtualDtor.size());
- }
-
- if (CP.MaybeRequireVectorDeletingDtorSize !=
- Ctx.MaybeRequireVectorDeletingDtor.size()) {
- llvm::dbgs() << "if (CP.MaybeRequireVectorDeletingDtorSize != "
- "Ctx.MaybeRequireVectorDeletingDtor.size()\n";
- eraseDenseSetIf(Ctx.MaybeRequireVectorDeletingDtor,
- [&](const CXXRecordDecl *RD) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(const_cast<CXXRecordDecl *>(RD)),
- SlabCP);
- });
- assert(CP.MaybeRequireVectorDeletingDtorSize ==
- Ctx.MaybeRequireVectorDeletingDtor.size());
- }
-
- if (CP.MergedDeclsSize != Ctx.MergedDecls.size()) {
- llvm::dbgs() << "CP.MergedDeclsSize != Ctx.MergedDecls.size()\n";
- assert(CP.MergedDeclsSize == Ctx.MergedDecls.size());
- }
-
- if (CP.DeclAttrsSize != Ctx.DeclAttrs.size()) {
- llvm::dbgs() << "CP.DeclAttrsSize != Ctx.DeclAttrs.size()\n";
- eraseDenseMapIf(
- Ctx.DeclAttrs,
- [&](llvm::detail::DenseMapPair<const Decl *, AttrVec *> &KV) -> bool {
- bool isAfter =
- Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP) ||
- Ctx.getAllocator().isAfterCheckpoint(KV.getSecond(), SlabCP);
- if (isAfter)
- KV.getSecond()->~AttrVec();
- return isAfter;
- });
- assert(CP.DeclAttrsSize == Ctx.DeclAttrs.size());
- }
-
- if (CP.MergedDefModulesSize != Ctx.MergedDefModules.size()) {
- llvm::dbgs() << "CP.MergedDefModulesSize != Ctx.MergedDefModules.size()\n";
- assert(CP.MergedDefModulesSize == Ctx.MergedDefModules.size());
- }
-
- if (CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()) {
- llvm::dbgs()
- << "CP.ModuleInitializersSize != Ctx.ModuleInitializers.size()\n";
- assert(CP.ModuleInitializersSize == Ctx.ModuleInitializers.size());
- }
-
- if (CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()) {
- llvm::dbgs()
- << "CP.PrimaryModuleNameMapSize != Ctx.PrimaryModuleNameMap.size()\n";
- assert(CP.PrimaryModuleNameMapSize == Ctx.PrimaryModuleNameMap.size());
- }
-
- if (CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()) {
- llvm::dbgs()
- << "CP.SameModuleLookupSetSize != Ctx.SameModuleLookupSet.size()\n";
- assert(CP.SameModuleLookupSetSize == Ctx.SameModuleLookupSet.size());
- }
-
- if (CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()) {
- llvm::dbgs() << "CP.ScalableVecTyMapSize != Ctx.ScalableVecTyMap.size()\n";
- assert(CP.ScalableVecTyMapSize == Ctx.ScalableVecTyMap.size());
- }
-
- if (CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()) {
- llvm::dbgs() << "CP.LambdaCastPathsSize != Ctx.LambdaCastPaths.size()\n";
- assert(CP.LambdaCastPathsSize == Ctx.LambdaCastPaths.size());
- }
-
- if (CP.RawCommentsSize != Ctx.RawComments.size()) {
- llvm::dbgs() << "CP.RawCommentsSize != Ctx.RawComments.size()\n";
- assert(CP.RawCommentsSize == Ctx.RawComments.size());
- }
-
- if (CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()) {
- llvm::dbgs()
- << "CP.RedeclChainCommentsSize != Ctx.RedeclChainComments.size()\n";
- assert(CP.RedeclChainCommentsSize == Ctx.RedeclChainComments.size());
- }
-
- if (CP.CommentlessRedeclChainsSize != Ctx.CommentlessRedeclChains.size()) {
- llvm::dbgs() << "CP.CommentlessRedeclChainsSize != "
- "Ctx.CommentlessRedeclChains.size()\n";
- assert(CP.CommentlessRedeclChainsSize ==
- Ctx.CommentlessRedeclChains.size());
- }
-
- if (CP.ParsedCommentsSize != Ctx.ParsedComments.size()) {
- llvm::dbgs() << "CP.ParsedCommentsSize != Ctx.ParsedComments.size()\n";
- assert(CP.ParsedCommentsSize == Ctx.ParsedComments.size());
- }
-
- if (CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()) {
- llvm::dbgs()
- << "CP.RelocatableClassesSize != Ctx.RelocatableClasses.size()\n";
- assert(CP.RelocatableClassesSize == Ctx.RelocatableClasses.size());
- }
-
- if (CP.ParamIndicesSize != Ctx.ParamIndices.size()) {
- llvm::dbgs() << "CP.ParamIndicesSize != Ctx.ParamIndices.size()\n";
- assert(CP.ParamIndicesSize == Ctx.ParamIndices.size());
- }
-
- if (CP.MangleNumbersSize != Ctx.MangleNumbers.size()) {
- llvm::dbgs() << "CP.MangleNumbersSize != Ctx.MangleNumbers.size()\n";
- assert(CP.MangleNumbersSize == Ctx.MangleNumbers.size());
- }
-
- if (CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()) {
- llvm::dbgs()
- << "CP.StaticLocalNumbersSize != Ctx.StaticLocalNumbers.size()\n";
- assert(CP.StaticLocalNumbersSize == Ctx.StaticLocalNumbers.size());
- }
-
- if (CP.TemplateOrInstantiationSize != Ctx.TemplateOrInstantiation.size()) {
- llvm::dbgs() << "CP.TemplateOrInstantiationSize != "
- "Ctx.TemplateOrInstantiation.size()\n";
- // llvm::DenseMap<const VarDecl *,
- // TemplateOrSpecializationInfo>
- // TemplateOrInstantiation;
- eraseDenseMapIf(
- Ctx.TemplateOrInstantiation,
- [&](llvm::detail::DenseMapPair<
- const VarDecl *, ASTContext::TemplateOrSpecializationInfo> &KV)
- -> bool {
- ASTContext::TemplateOrSpecializationInfo &T = KV.getSecond();
- bool IsAfterCP = false;
- if (auto *M = T.dyn_cast<MemberSpecializationInfo *>())
- IsAfterCP = Ctx.getAllocator().isAfterCheckpoint(M, SlabCP);
- if (auto *V = T.dyn_cast<VarTemplateDecl *>())
- IsAfterCP = Ctx.getAllocator().isAfterCheckpoint(V, SlabCP);
- return IsAfterCP ||
- Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
- });
- assert(CP.TemplateOrInstantiationSize ==
- Ctx.TemplateOrInstantiation.size());
- }
-
- if (CP.InstantiatedFromUsingDeclSize !=
- Ctx.InstantiatedFromUsingDecl.size()) {
- llvm::dbgs() << "CP.InstantiatedFromUsingDeclSize != "
- "Ctx.InstantiatedFromUsingDecl.size()\n";
- assert(CP.InstantiatedFromUsingDeclSize ==
- Ctx.InstantiatedFromUsingDecl.size());
- }
-
- if (CP.InstantiatedFromUsingEnumDeclSize !=
- Ctx.InstantiatedFromUsingEnumDecl.size()) {
- llvm::dbgs() << "CP.InstantiatedFromUsingEnumDeclSize != "
- "Ctx.InstantiatedFromUsingEnumDecl.size()\n";
- assert(CP.InstantiatedFromUsingEnumDeclSize ==
- Ctx.InstantiatedFromUsingEnumDecl.size());
- }
-
- if (CP.InstantiatedFromUsingShadowDeclSize !=
- Ctx.InstantiatedFromUsingShadowDecl.size()) {
- llvm::dbgs() << "CP.InstantiatedFromUsingShadowDeclSize != "
- "Ctx.InstantiatedFromUsingShadowDecl.size()\n";
- assert(CP.InstantiatedFromUsingShadowDeclSize ==
- Ctx.InstantiatedFromUsingShadowDecl.size());
- }
-
- if (CP.InstantiatedFromUnnamedFieldDeclSize !=
- Ctx.InstantiatedFromUnnamedFieldDecl.size()) {
- llvm::dbgs() << "CP.InstantiatedFromUnnamedFieldDeclSize != "
- "Ctx.InstantiatedFromUnnamedFieldDecl.size()\n";
- assert(CP.InstantiatedFromUnnamedFieldDeclSize ==
- Ctx.InstantiatedFromUnnamedFieldDecl.size());
- }
-
- if (CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()) {
- llvm::dbgs()
- << "CP.OverriddenMethodsSize != Ctx.OverriddenMethods.size()\n";
- // using CXXMethodVector = llvm::TinyPtrVector<const CXXMethodDecl *>;
- // llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector> OverriddenMethods;
- // eraseDenseMapIf(
- // Ctx.OverriddenMethods,
- // [&](llvm::detail::DenseMapPair<const CXXMethodDecl *,
- // CXXMethodVector>
- // &KV) -> bool {
- // bool isAfter = Ctx.getAllocater().isAfterCheckpoint(
- // static_cast<void *>(const_cast<CXXMethodDecl
- // *>(KV.getFirst())), SlabCP);
- // if (!isAfter) {
- // std::vector<CXXMethodDecl *> ToBeRemoved;
- // ToBeRemoved.reserve(KV.getSecond().size());
- // for (auto *M : std::reverse(KV.getSecond()))
- // if (Ctx.getAllocater().isAfterCheckpoint(
- // static_cast<void *>(const_cast<CXXMethodDecl *>(M)),
- // SlabCP)) {
- // ToBeRemoved.push_back(M);
- // }
- // for (auto *M : ToBeRemoved)
- // KV.getSecond().erase(M);
- // }
- // return isAfter;
- // });
-
- for (auto &KV : Ctx.OverriddenMethods) {
- if (Ctx.getAllocator().isAfterCheckpoint(KV.first, SlabCP))
- continue; // entire entry will be removed in second pass
-
- auto &Vec = KV.getSecond();
- llvm::SmallVector<const CXXMethodDecl *, 4> ToRemove;
- for (auto *M : Vec)
- if (Ctx.getAllocator().isAfterCheckpoint(M, SlabCP))
- ToRemove.push_back(M);
-
- for (auto *M : ToRemove)
- Vec.erase(llvm::find(Vec, M));
- }
-
- eraseDenseMapIf(
- Ctx.OverriddenMethods,
- [&](llvm::detail::DenseMapPair<
- const CXXMethodDecl *, ASTContext::CXXMethodVector> &KV) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
- });
- assert(CP.OverriddenMethodsSize == Ctx.OverriddenMethods.size());
- }
-
- if (CP.MangleNumberingContextsSize != Ctx.MangleNumberingContexts.size()) {
- llvm::dbgs() << "CP.MangleNumberingContextsSize != "
- "Ctx.MangleNumberingContexts.size()\n";
- eraseDenseMapIf(
- Ctx.MangleNumberingContexts,
- [&](llvm::detail::DenseMapPair<
- const DeclContext *, std::unique_ptr<MangleNumberingContext>> &KV)
- -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(KV.getFirst(), SlabCP);
- });
- assert(CP.MangleNumberingContextsSize ==
- Ctx.MangleNumberingContexts.size());
- }
-
- if (CP.ExtraMangleNumberingContextsSize !=
- Ctx.ExtraMangleNumberingContexts.size()) {
- llvm::dbgs() << "CP.ExtraMangleNumberingContextsSize != "
- "Ctx.ExtraMangleNumberingContexts.size()\n";
- assert(CP.ExtraMangleNumberingContextsSize ==
- Ctx.ExtraMangleNumberingContexts.size());
- }
-
- if (CP.TraversalScopeSize != Ctx.TraversalScope.size()) {
- llvm::dbgs() << "CP.TraversalScopeSize != "
- "Ctx.TraversalScope.size()\n";
- assert(CP.TraversalScopeSize == Ctx.TraversalScope.size());
- }
-
- /// Obj-C
- if (CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()) {
- llvm::dbgs() << "CP.ObjCObjectTypesSize != Ctx.ObjCObjectTypes.size()\n";
- eraseFoldingSetIf(Ctx.ObjCObjectTypes,
- [&](ObjCObjectTypeImpl &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(CP.ObjCObjectTypesSize == Ctx.ObjCObjectTypes.size());
- }
-
- if (CP.ObjCObjectPointerTypesSize != Ctx.ObjCObjectPointerTypes.size()) {
- llvm::dbgs() << "CP.ObjCObjectPointerTypesSize != "
- "Ctx.ObjCObjectPointerTypes.size()\n";
- eraseFoldingSetIf(Ctx.ObjCObjectPointerTypes,
- [&](ObjCObjectPointerType &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(&Node), SlabCP);
- });
- assert(CP.ObjCObjectPointerTypesSize == Ctx.ObjCObjectPointerTypes.size());
- }
-
- if (CP.ObjCIdDeclCP != Ctx.ObjCIdDecl) {
- llvm::dbgs() << "CP.ObjCIdDeclCP != Ctx.ObjCIdDecl\n";
- Ctx.ObjCIdDecl = CP.ObjCIdDeclCP;
- assert(CP.ObjCIdDeclCP == Ctx.ObjCIdDecl);
- }
- if (CP.ObjCSelDeclCP != Ctx.ObjCSelDecl) {
- llvm::dbgs() << "CP.ObjCSelDeclCP != Ctx.ObjCSelDecl\n";
- Ctx.ObjCSelDecl = CP.ObjCSelDeclCP;
- assert(CP.ObjCSelDeclCP == Ctx.ObjCSelDecl);
- }
- if (CP.ObjCClassDeclCP != Ctx.ObjCClassDecl) {
- llvm::dbgs() << "CP.ObjCClassDeclCP != Ctx.ObjCClassDecl\n";
- Ctx.ObjCClassDecl = CP.ObjCClassDeclCP;
- assert(CP.ObjCClassDeclCP == Ctx.ObjCClassDecl);
- }
- if (CP.ObjCProtocolClassDeclCP != Ctx.ObjCProtocolClassDecl) {
- llvm::dbgs() << "CP.ObjCProtocolClassDeclCP != Ctx.ObjCProtocolClassDecl\n";
- Ctx.ObjCProtocolClassDecl = CP.ObjCProtocolClassDeclCP;
- assert(CP.ObjCProtocolClassDeclCP == Ctx.ObjCProtocolClassDecl);
- }
-
- if (CP.DeclarationNames.CXXConstructorNamesSize !=
- Ctx.DeclarationNames.CXXConstructorNames.size()) {
- llvm::dbgs() << "CP.DeclarationNames.CXXConstructorNamesSize != "
- "Ctx.DeclarationNames.CXXConstructorNames.size()";
- eraseFoldingSetIf(Ctx.DeclarationNames.CXXConstructorNames,
- [&](detail::CXXSpecialNameExtra &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(&Node,
- SlabCP);
- });
- assert(CP.DeclarationNames.CXXConstructorNamesSize ==
- Ctx.DeclarationNames.CXXConstructorNames.size());
- }
- if (CP.DeclarationNames.CXXDestructorNamesSize !=
- Ctx.DeclarationNames.CXXDestructorNames.size()) {
- llvm::dbgs() << "CP.DeclarationNames.CXXDestructorNamesSize != "
- "Ctx.DeclarationNames.CXXDestructorNames.size()";
- eraseFoldingSetIf(Ctx.DeclarationNames.CXXDestructorNames,
- [&](detail::CXXSpecialNameExtra &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(&Node,
- SlabCP);
- });
- assert(CP.DeclarationNames.CXXDestructorNamesSize ==
- Ctx.DeclarationNames.CXXDestructorNames.size());
- }
- if (CP.DeclarationNames.CXXConversionFunctionNamesSize !=
- Ctx.DeclarationNames.CXXConversionFunctionNames.size()) {
- llvm::dbgs() << "CP.DeclarationNames.CXXConversionFunctionNamesSize != "
- "Ctx.DeclarationNames.CXXConversionFunctionNames.size()";
- eraseFoldingSetIf(Ctx.DeclarationNames.CXXConversionFunctionNames,
- [&](detail::CXXSpecialNameExtra &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(&Node,
- SlabCP);
- });
- assert(CP.DeclarationNames.CXXConversionFunctionNamesSize ==
- Ctx.DeclarationNames.CXXConversionFunctionNames.size());
- }
- if (CP.DeclarationNames.CXXLiteralOperatorNamesSize !=
- Ctx.DeclarationNames.CXXLiteralOperatorNames.size()) {
- llvm::dbgs() << "CP.DeclarationNames.CXXLiteralOperatorNamesSize != "
- "Ctx.DeclarationNames.CXXLiteralOperatorNames.size()";
- eraseFoldingSetIf(Ctx.DeclarationNames.CXXLiteralOperatorNames,
- [&](detail::CXXLiteralOperatorIdName &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(&Node,
- SlabCP);
- });
- assert(CP.DeclarationNames.CXXLiteralOperatorNamesSize ==
- Ctx.DeclarationNames.CXXLiteralOperatorNames.size());
- }
- if (CP.DeclarationNames.CXXDeductionGuideNamesSize !=
- Ctx.DeclarationNames.CXXDeductionGuideNames.size()) {
- llvm::dbgs() << "CP.DeclarationNames.CXXDeductionGuideNamesSize != "
- "Ctx.DeclarationNames.CXXDeductionGuideNames.size()";
- eraseFoldingSetIf(Ctx.DeclarationNames.CXXDeductionGuideNames,
- [&](detail::CXXDeductionGuideNameExtra &Node) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(&Node,
- SlabCP);
- });
- assert(CP.DeclarationNames.CXXDeductionGuideNamesSize ==
- Ctx.DeclarationNames.CXXDeductionGuideNames.size());
- }
- for (unsigned I = 0; I < NUM_OVERLOADED_OPERATORS; I++)
- Ctx.DeclarationNames.CXXOperatorNames[I] =
- CP.DeclarationNames.CXXOperatorNamesCP[I];
-
- // for (auto &[Decl, OldTy] : Ctx.PendingTypeForDeclMutations)
- // Decl->TypeForDecl = OldTy;
-
- // Ctx.PendingTypeForDeclMutations.clear();
- // TranslationUnitDecl *MostRecentTU = Ctx.getTranslationUnitDecl();
- // for (const auto *DC : Ctx.PendingDCMutations) {
- // // if (MostRecentTU->getPrimaryContext() == DC)
- // // continue;
- // if (StoredDeclsMap *Map = const_cast<DeclContext *>(DC)
- // ->getPrimaryContext()
- // ->getLookupPtr()) {
- // for (auto &&[Key, List] : *Map) {
- // DeclContextLookupResult R = List.getLookupResult();
- // std::vector<NamedDecl *> NamedDeclsToRemove;
- // // bool RemoveAll = true;
- // for (NamedDecl *D : R) {
- // // llvm::outs() << "D->getTranslationUnitDecl() == MostRecentTU ("
- // <<
- // // (D->getTranslationUnitDecl() == MostRecentTU) << ")\n";
- // // llvm::outs() << "DeclContext : " << DC << "\n";
- // // D->dump();
- // // if (D->getTranslationUnitDecl() == MostRecentTU)
- // if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D),
- // SlabCP))
- // NamedDeclsToRemove.push_back(D);
- // // else
- // // RemoveAll = false;
- // }
- // // if (LLVM_LIKELY(RemoveAll)) {
- // // Map->erase(Key);
- // // } else {
- // for (NamedDecl *D : NamedDeclsToRemove)
- // List.remove(D);
- // // }
- // }
- // }
-
- // Decl *Prev = DC->FirstDecl;
- // Decl *Cur = DC->FirstDecl;
- // while (Cur) {
- // if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(Cur),
- // SlabCP)) {
- // DC->LastDecl = Prev;
- // DC->LastDecl->NextInContextAndBits.setPointer(nullptr);
- // break;
- // }
- // Prev = Cur;
- // Cur = Cur->getNextDeclInContext();
- // }
- // }
-
- // if (FirstDecl) {
- // LastDecl->NextInContextAndBits.setPointer(D);
- // LastDecl = D;
- // } else {
- // FirstDecl = LastDecl = D;
- // }
-
- // Notify a C++ record declaration that we've added a member, so it can
- // update its class-specific state.
- // if (auto *Record = dyn_cast<CXXRecordDecl>(this))
- // Record->addedMember(D);
-
- // ImportDecl *FirstLocalImport = nullptr;
- // ImportDecl *LastLocalImport = nullptr;
-
- // class ImportDecl final : public Decl,
- // llvm::TrailingObjects<ImportDecl, SourceLocation> {
- // friend class ASTContext;
- // friend class ASTDeclReader;
- // friend class ASTReader;
- // friend TrailingObjects;
-
- // /// The imported module.
- // Module *ImportedModule = nullptr;
-
- // /// The next import in the list of imports local to the translation
- // /// unit being parsed (not loaded from an AST file).
- // ///
- // /// Includes a bit that indicates whether we have source-location
- // information
- // /// for each identifier in the module name.
- // ///
- // /// When the bit is false, we only have a single source location for the
- // /// end of the import declaration.
- // llvm::PointerIntPair<ImportDecl *, 1, bool> NextLocalImportAndComplete;
-
- // llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM = Ctx.LastSDM;
-
- // StoredDeclsMap *Map = LastSDM.getPointer();
- // bool Dependent = LastSDM.getInt();
- // while (Map && (Map != CP.LastSDM.getPointer())) {
- // // llvm::outs() << "we are deleting LASTSDM\n";
- // // // Advance the iteration before we invalidate memory.
- // llvm::PointerIntPair<StoredDeclsMap*,1> Next = Map->Previous;
-
- // if (Dependent)
- // delete static_cast<DependentStoredDeclsMap*>(Map);
- // else
- // delete Map;
-
- // Map = Next.getPointer();
- // Dependent = Next.getInt();
- // }
-
- // Ctx.LastSDM = CP.LastSDM;
- DeclListNode *FreeNodeHead = Ctx.ListNodeFreeList;
- while (FreeNodeHead) {
- if (!Ctx.getAllocator().isAfterCheckpoint(FreeNodeHead, SlabCP))
- break;
- FreeNodeHead = dyn_cast_if_present<DeclListNode *>(FreeNodeHead->Rest);
- }
-
- Ctx.ListNodeFreeList = FreeNodeHead;
-
- Ctx.Types.resize(CP.TypesSize);
-}
-
-void ASTContextStateRecovery::commit() {}
-} // end namespace clang
\ No newline at end of file
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index ab8e418e8d731..01d3295d1ac30 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -20,14 +20,12 @@ if (EMSCRIPTEN AND "lld" IN_LIST LLVM_ENABLE_PROJECTS)
endif()
add_clang_library(clangInterpreter
- ASTContextStateStash.cpp
DeviceOffload.cpp
CodeCompletion.cpp
IncrementalAction.cpp
IncrementalExecutor.cpp
OrcIncrementalExecutor.cpp
IncrementalParser.cpp
- SemaStateStash.cpp
Interpreter.cpp
InterpreterValuePrinter.cpp
InterpreterUtils.cpp
diff --git a/clang/lib/Interpreter/IncrementalAction.h b/clang/lib/Interpreter/IncrementalAction.h
index c3e4f108ea365..2893ff7b5baa6 100644
--- a/clang/lib/Interpreter/IncrementalAction.h
+++ b/clang/lib/Interpreter/IncrementalAction.h
@@ -36,7 +36,6 @@ class IncrementalAction : public WrapperFrontendAction {
bool IsTerminating = false;
Interpreter &Interp;
[[maybe_unused]] CompilerInstance &CI;
- // llvm::LLVMContext &LLVMCtx;
std::unique_ptr<ASTConsumer> Consumer;
/// When CodeGen is created the first llvm::Module gets cached in many places
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index ed8919d8af59e..fac67bf00ed14 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -93,7 +93,7 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
DiagnosticsEngine &Diags = S.getDiagnostics();
if (Diags.hasErrorOccurred()) {
CleanUpPTU(C.getTranslationUnitDecl());
- // Consumer->HandleTranslationUnit(C);
+
Diags.Reset(/*soft=*/true);
Diags.getClient()->clear();
return llvm::make_error<llvm::StringError>("Parsing failed.",
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index bf9d20653727a..c03881b0ed336 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -568,37 +568,29 @@ size_t Interpreter::getEffectivePTUSize() const {
llvm::Expected<PartialTranslationUnit &>
Interpreter::Parse(llvm::StringRef Code) {
- class PTUSlabRollback {
+ class PTURollbackGuard {
public:
- explicit PTUSlabRollback(Sema &S)
- : Ctx(S.getASTContext()), ASTCtxState(Ctx), SemaState(S),
+ explicit PTURollbackGuard(Sema &S)
+ : Ctx(S.getASTContext()),
CheckPoint(Ctx.getAllocator().checkPoint()) {
- SemaState.stash(SemaCheckPoint);
- ASTCtxState.stash(CtxCheckPoint);
}
- ~PTUSlabRollback() {
+ ~PTURollbackGuard() {
if (!Committed) {
- SemaState.restore(SemaCheckPoint, CheckPoint);
- ASTCtxState.restore(CtxCheckPoint, CheckPoint);
- Ctx.getAllocator().restoreToCheckPoint(CheckPoint);
+ // TODO add memory restore support.
+ // Ctx.getAllocator().restoreToCheckPoint(CheckPoint);
}
}
void commit(PartialTranslationUnit &PTU) {
- ASTCtxState.commit();
PTU.SlabCheckPoint = CheckPoint;
Committed = true;
}
private:
ASTContext &Ctx;
- ASTContextStateRecovery ASTCtxState;
- SemaStateRecovery SemaState;
llvm::SlabCheckPoint CheckPoint;
bool Committed = false;
- StashCheckPoint CtxCheckPoint;
- SemaStashCheckPoint SemaCheckPoint;
};
// If we have a device parser, parse it first. The generated code will be
@@ -627,10 +619,8 @@ Interpreter::Parse(llvm::StringRef Code) {
// PTUSlabRollback Rollback(CI->getSema());
llvm::Expected<TranslationUnitDecl *> TuOrErr = IncrParser->Parse(Code);
- if (!TuOrErr) {
- // Act->GenModule();
+ if (!TuOrErr)
return TuOrErr.takeError();
- }
PartialTranslationUnit &LastPTU = IncrParser->RegisterPTU(*TuOrErr);
diff --git a/clang/lib/Interpreter/SemaStateStash.cpp b/clang/lib/Interpreter/SemaStateStash.cpp
deleted file mode 100644
index 1c24b634b9443..0000000000000
--- a/clang/lib/Interpreter/SemaStateStash.cpp
+++ /dev/null
@@ -1,918 +0,0 @@
-//===--- SemaStateRecovery.cpp - Sema persistent state stash/restore
-//----------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Interpreter/ErrorRecovery.h"
-
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/DeclTemplate.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/ExprCXX.h"
-#include "clang/AST/Type.h"
-#include "clang/Basic/IdentifierTable.h"
-#include "clang/Sema/Sema.h"
-#include "clang/Sema/SemaInternal.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include "llvm/ADT/SmallSet.h"
-#include "llvm/ADT/SmallVector.h"
-
-namespace clang {
-
-template <typename EntryType, typename PredT>
-static void eraseFoldingSetIf(llvm::FoldingSet<EntryType> &FS, PredT &&Pred) {
- SmallVector<EntryType *, 16> ToRemove;
- for (auto &N : FS)
- if (Pred(N))
- ToRemove.push_back(&N);
- for (auto *N : ToRemove)
- FS.RemoveNode(N);
-}
-
-// template <typename EntryType, typename PredT>
-// static void eraseContextualFoldingSetIf(
-// llvm::ContextualFoldingSet<EntryType, const ASTContext &> &FS,
-// PredT &&Pred) {
-// SmallVector<EntryType *, 16> ToRemove;
-// for (auto &N : FS)
-// if (Pred(N))
-// ToRemove.push_back(&N);
-// for (auto *N : ToRemove)
-// FS.RemoveNode(N);
-// }
-
-/// Erase from DenseMap based on predicate
-template <typename KeyT, typename ValueT, typename PredT>
-static void eraseDenseMapIf(llvm::DenseMap<KeyT, ValueT> &Map, PredT &&Pred) {
- SmallVector<KeyT, 16> ToRemove;
-
- for (auto &KV : Map)
- if (Pred(KV))
- ToRemove.push_back(KV.getFirst());
-
- for (auto &Key : ToRemove)
- Map.erase(Key);
-}
-
-// template <typename ValueT, typename PredT>
-// static void eraseDenseSetIf(llvm::DenseSet<ValueT> &Set, PredT &&Pred) {
-// SmallVector<ValueT, 16> ToRemove;
-// for (const auto &Val : Set)
-// if (Pred(Val))
-// ToRemove.push_back(Val);
-// for (const auto &Val : ToRemove)
-// Set.erase(Val);
-// }
-
-template <typename T, unsigned SmallSize, typename PredT>
-static void eraseSmallPtrSetIf(llvm::SmallPtrSet<T, SmallSize> &Set,
- PredT &&Pred) {
- SmallVector<T, 8> ToRemove;
- for (T Val : Set)
- if (Pred(Val))
- ToRemove.push_back(Val);
- for (T Val : ToRemove)
- Set.erase(Val);
-}
-
-// template <typename T, unsigned N, typename PredT>
-// static void eraseSmallSetVectorIf(llvm::SmallSetVector<T, N> &SV,
-// PredT &&Pred) {
-// SmallVector<T, 8> ToRemove;
-// for (const T &Val : SV)
-// if (Pred(Val))
-// ToRemove.push_back(Val);
-// for (const T &Val : ToRemove)
-// SV.remove(Val);
-// }
-
-// template <typename KeyT, typename ValueT, typename PredT>
-// static void eraseMapVectorIf(llvm::MapVector<KeyT, ValueT> &MV, PredT &&Pred)
-// {
-// SmallVector<KeyT, 16> ToRemove;
-// for (auto &KV : MV)
-// if (Pred(KV))
-// ToRemove.push_back(KV.first);
-// for (const auto &Key : ToRemove)
-// MV.erase(Key);
-// }
-
-// template <typename T, typename PredT>
-// static void eraseVectorIf(SmallVectorImpl<T> &Vec, PredT &&Pred) {
-// llvm::erase_if(Vec, std::forward<PredT>(Pred));
-// }
-
-//===----------------------------------------------------------------------===//
-// Pragma / value snapshot (uses Sema friend access for nested types)
-//===----------------------------------------------------------------------===//
-
-// struct SemaStateRecovery::PragmaSnapshot {
-// Sema::PragmaClangSection PragmaClangBSSSection;
-// Sema::PragmaClangSection PragmaClangDataSection;
-// Sema::PragmaClangSection PragmaClangRodataSection;
-// Sema::PragmaClangSection PragmaClangRelroSection;
-// Sema::PragmaClangSection PragmaClangTextSection;
-
-// Sema::PragmaStack<MSVtorDispMode> VtorDispStack;
-// Sema::PragmaStack<Sema::AlignPackInfo> AlignPackStack;
-// Sema::PragmaStack<StringLiteral *> DataSegStack;
-// Sema::PragmaStack<StringLiteral *> BSSSegStack;
-// Sema::PragmaStack<StringLiteral *> ConstSegStack;
-// Sema::PragmaStack<StringLiteral *> CodeSegStack;
-// Sema::PragmaStack<bool> StrictGuardStackCheckStack;
-// Sema::PragmaStack<FPOptionsOverride> FpPragmaStack;
-
-// StringLiteral *CurInitSeg = nullptr;
-// SourceLocation CurInitSegLoc;
-// bool MSPragmaOptimizeIsOn = true;
-// SourceLocation OptimizeOffPragmaLocation;
-
-// FileNullabilityMap NullabilityMap;
-// };
-
-void SemaStateRecovery::stash(SemaStashCheckPoint &CP) {
- CP.SemaBumpSlabCP = S.BumpAlloc.checkPoint();
- // CP.CachedFunctionScopeSize = S.CachedFunctionScope.size();
- CP.FunctionScopesSize = S.FunctionScopes.size();
-
- // CP.Ident_superSize = S.Ident_super.size();
-
- /// --- Pragma ---
- // CP.PragmaClangBSSSectionSize = S.PragmaClangBSSSection.size();
- // CP.PragmaClangDataSectionSize = S.PragmaClangDataSection.size();
- // CP.PragmaClangRodataSectionSize = S.PragmaClangRodataSection.size();
- // CP.PragmaClangRelroSectionSize = S.PragmaClangRelroSection.size();
- // CP.PragmaClangTextSectionSize = S.PragmaClangTextSection.size();
- // CP.VtorDispStackSize = S.VtorDispStack.size();
- // CP.AlignPackStackSize = S.AlignPackStack.size();
- // CP.AlignPackIncludeStackSize = S.AlignPackIncludeStack.size();
- // CP.DataSegStackSize = S.DataSegStack.size();
- // CP.BSSSegStackSize = S.BSSSegStack.size();
- // CP.ConstSegStackSize = S.ConstSegStack.size();
- // CP.CodeSegStackSize = S.CodeSegStack.size();
- // CP.StrictGuardStackCheckStackSize = S.StrictGuardStackCheckStack.size();
- // CP.FpPragmaStackSize = S.FpPragmaStack.size();
-
- // CP.FunctionToSectionMapSize = S.FunctionToSectionMap.size();
- // CP.PragmaAttributeStackSize = S.PragmaAttributeStack.size();
- // CP.MSFunctionNoBuiltinsSize = S.MSFunctionNoBuiltins.size();
- // CP.PendingExportedNamesSize = S.PendingExportedNames.size();
- CP.TypoCorrectedFunctionDefinitionsSize =
- S.TypoCorrectedFunctionDefinitions.size();
- CP.FlagBitsCacheSize = S.FlagBitsCache.size();
- CP.AssignEnumCacheSize = S.AssignEnumCache.size();
- CP.WeakUndeclaredIdentifiersSize = S.WeakUndeclaredIdentifiers.size();
- CP.ExtnameUndeclaredIdentifiersSize = S.ExtnameUndeclaredIdentifiers.size();
- CP.UnusedLocalTypedefNameCandidatesSize =
- S.UnusedLocalTypedefNameCandidates.size();
- CP.UnusedFileScopedDeclsSize = S.UnusedFileScopedDecls.end();
- CP.TentativeDefinitionsSize = S.TentativeDefinitions.end();
- CP.ExternalDeclarationsSize = S.ExternalDeclarations.size();
- CP.ParsingInitForAutoVarsSize = S.ParsingInitForAutoVars.size();
- CP.DeclsToCheckForDeferredDiagsSize = S.DeclsToCheckForDeferredDiags.size();
- CP.ShadowingDeclsSize = S.ShadowingDecls.size();
- CP.WeakTopLevelDeclSize = S.WeakTopLevelDecl.size();
- CP.ExtVectorDeclsSize = S.ExtVectorDecls.end();
- CP.VTableUsesSize = S.VTableUses.size();
- CP.VTablesUsedSize = S.VTablesUsed.size();
- CP.DelayedDllExportClassesSize = S.DelayedDllExportClasses.size();
- CP.DelayedDllExportMemberFunctionsSize =
- S.DelayedDllExportMemberFunctions.size();
- CP.InventedParameterInfosSize = S.InventedParameterInfos.size();
- // CP.FieldCollectorSize = S.FieldCollector.size();
- CP.UnusedPrivateFieldsSize = S.UnusedPrivateFields.size();
- if (S.PureVirtualClassDiagSet)
- CP.PureVirtualClassDiagSetSize = S.PureVirtualClassDiagSet->size();
- CP.DelegatingCtorDeclsSize = S.DelegatingCtorDecls.end();
- // CP.StdNamespaceSize = S.StdNamespace.size();
- CP.UnparsedDefaultArgLocsSize = S.UnparsedDefaultArgLocs.size();
- CP.UndefinedButUsedSize = S.UndefinedButUsed.size();
- CP.SpecialMembersBeingDeclaredSize = S.SpecialMembersBeingDeclared.size();
- CP.DelayedOverridingExceptionSpecChecksSize =
- S.DelayedOverridingExceptionSpecChecks.size();
- CP.DelayedEquivalentExceptionSpecChecksSize =
- S.DelayedEquivalentExceptionSpecChecks.size();
- CP.MaybeODRUseExprsSize = S.MaybeODRUseExprs.size();
- CP.RefsMinusAssignmentsSize = S.RefsMinusAssignments.size();
- CP.ExprCleanupObjectsSize = S.ExprCleanupObjects.size();
- CP.ExprEvalContextsSize = S.ExprEvalContexts.size();
- CP.FailedImmediateInvocationsSize = S.FailedImmediateInvocations.size();
- // CP.ImplicitlyRetainedSelfLocsSize = S.ImplicitlyRetainedSelfLocs.size();
- CP.DeleteExprsSize = S.DeleteExprs.size();
- CP.CurrentParameterCopyTypesSize = S.CurrentParameterCopyTypes.size();
- CP.AggregateDeductionCandidatesSize = S.AggregateDeductionCandidates.size();
- CP.TypoCorrectionFailuresSize = S.TypoCorrectionFailures.size();
- CP.SpecialMemberCacheSize = S.SpecialMemberCache.size();
- CP.ModuleScopesSize = S.ModuleScopes.size();
- CP.DeferredExportedNamespacesSize = S.DeferredExportedNamespaces.size();
- CP.PendingInlineFuncDeclsSize = S.PendingInlineFuncDecls.size();
- CP.CurrentSEHFinallySize = S.CurrentSEHFinally.size();
- CP.CurrentDeferSize = S.CurrentDefer.size();
- CP.LateParsedTemplateMapSize = S.LateParsedTemplateMap.size();
- CP.SuppressedDiagnosticsSize = S.SuppressedDiagnostics.size();
- // CP.CurrentInstantiationScopeSize = S.CurrentInstantiationScope.size();
- CP.UnparsedDefaultArgInstantiationsSize =
- S.UnparsedDefaultArgInstantiations.size();
- CP.CodeSynthesisContextsSize = S.CodeSynthesisContexts.size();
- CP.InstantiatingSpecializationsSize = S.InstantiatingSpecializations.size();
- CP.InstantiatedNonDependentTypesSize = S.InstantiatedNonDependentTypes.size();
- CP.CodeSynthesisContextLookupModulesSize =
- S.CodeSynthesisContextLookupModules.size();
- CP.LookupModulesCacheSize = S.LookupModulesCache.size();
- CP.VisibleNamespaceCacheSize = S.VisibleNamespaceCache.size();
- // CP.TemplateInstCallbacksSize = S.TemplateInstCallbacks.size();
- CP.PendingInstantiationsSize = S.PendingInstantiations.size();
- CP.LateParsedInstantiationsSize = S.LateParsedInstantiations.size();
- CP.SavedVTableUsesSize = S.SavedVTableUses.size();
- CP.SavedPendingInstantiationsSize = S.SavedPendingInstantiations.size();
- CP.PendingLocalImplicitInstantiationsSize =
- S.PendingLocalImplicitInstantiations.size();
- CP.UnsubstitutedConstraintSatisfactionCacheSize =
- S.UnsubstitutedConstraintSatisfactionCache.size();
- if (S.CurrentCachedTemplateArgs)
- CP.CurrentCachedTemplateArgsSize = S.CurrentCachedTemplateArgs->size();
- CP.SubsumptionCacheSize = S.SubsumptionCache.size();
- CP.NormalizationCacheSize = S.NormalizationCache.size();
- CP.SatisfactionCacheSize = S.SatisfactionCache.size();
- CP.SatisfactionStackSize = S.SatisfactionStack.size();
- // CP.NullabilityMapSize = S.NullabilityMap.size();
- CP.DeclsWithEffectsToVerifySize = S.DeclsWithEffectsToVerify.size();
- // CP.AllEffectsToVerifySize = S.AllEffectsToVerify.size();
-}
-
-void SemaStateRecovery::restore(SemaStashCheckPoint &CP,
- llvm::SlabCheckPoint SlabCP) {
-
- ASTContext &Ctx = S.getASTContext();
- if (CP.FunctionScopesSize != S.FunctionScopes.size()) {
- llvm::dbgs() << "CP.FunctionScopesSize != S.FunctionScopes.size()\n";
- S.FunctionScopes.resize(CP.FunctionScopesSize);
- assert(CP.FunctionScopesSize == S.FunctionScopes.size());
- }
-
- if (CP.TypoCorrectedFunctionDefinitionsSize !=
- S.TypoCorrectedFunctionDefinitions.size()) {
- llvm::dbgs() << "CP.TypoCorrectedFunctionDefinitionsSize != "
- "S.TypoCorrectedFunctionDefinitions.size()\n";
- // llvm::SmallPtrSet<const NamedDecl *, 4> TypoCorrectedFunctionDefinitions;
- assert(CP.TypoCorrectedFunctionDefinitionsSize ==
- S.TypoCorrectedFunctionDefinitions.size());
- }
-
- if (CP.FlagBitsCacheSize != S.FlagBitsCache.size()) {
- llvm::dbgs() << "CP.FlagBitsCacheSize != S.FlagBitsCache.size()\n";
- // mutable llvm::DenseMap<const EnumDecl *, llvm::APInt> FlagBitsCache;
- assert(CP.FlagBitsCacheSize == S.FlagBitsCache.size());
- }
-
- if (CP.AssignEnumCacheSize != S.AssignEnumCache.size()) {
- llvm::dbgs() << "CP.AssignEnumCacheSize != S.AssignEnumCache.size()\n";
- // llvm::DenseMap<const EnumDecl *, llvm::SmallVector<llvm::APSInt>>
- // AssignEnumCache;
- assert(CP.AssignEnumCacheSize == S.AssignEnumCache.size());
- }
-
- if (CP.WeakUndeclaredIdentifiersSize != S.WeakUndeclaredIdentifiers.size()) {
- llvm::dbgs() << "CP.WeakUndeclaredIdentifiersSize != "
- "S.WeakUndeclaredIdentifiers.size()\n";
- // llvm::MapVector<
- // IdentifierInfo *,
- // llvm::SetVector<
- // WeakInfo, llvm::SmallVector<WeakInfo, 1u>,
- // llvm::SmallDenseSet<WeakInfo, 2u,
- // WeakInfo::DenseMapInfoByAliasOnly>>>
- // WeakUndeclaredIdentifiers;
- assert(CP.WeakUndeclaredIdentifiersSize ==
- S.WeakUndeclaredIdentifiers.size());
- }
-
- if (CP.ExtnameUndeclaredIdentifiersSize !=
- S.ExtnameUndeclaredIdentifiers.size()) {
- llvm::dbgs() << "CP.ExtnameUndeclaredIdentifiersSize != "
- "S.ExtnameUndeclaredIdentifiers.size()\n";
- // llvm::DenseMap<IdentifierInfo *, AsmLabelAttr *>
- // ExtnameUndeclaredIdentifiers;
- assert(CP.ExtnameUndeclaredIdentifiersSize ==
- S.ExtnameUndeclaredIdentifiers.size());
- }
-
- if (CP.UnusedLocalTypedefNameCandidatesSize !=
- S.UnusedLocalTypedefNameCandidates.size()) {
- llvm::dbgs() << "CP.UnusedLocalTypedefNameCandidatesSize != "
- "S.UnusedLocalTypedefNameCandidates.size()\n";
- // llvm::SmallSetVector<const TypedefNameDecl *, 4>
- // UnusedLocalTypedefNameCandidates;
- assert(CP.UnusedLocalTypedefNameCandidatesSize ==
- S.UnusedLocalTypedefNameCandidates.size());
- }
-
- if (CP.UnusedFileScopedDeclsSize != S.UnusedFileScopedDecls.end()) {
- llvm::dbgs()
- << "CP.UnusedFileScopedDeclsSize != S.UnusedFileScopedDecls.end()\n";
- // typedef LazyVector<const DeclaratorDecl *, ExternalSemaSource,
- // &ExternalSemaSource::ReadUnusedFileScopedDecls, 2, 2>
- // UnusedFileScopedDeclsType;
- assert(CP.UnusedFileScopedDeclsSize == S.UnusedFileScopedDecls.end());
- }
-
- if (CP.TentativeDefinitionsSize != S.TentativeDefinitions.end()) {
- llvm::dbgs()
- << "CP.TentativeDefinitionsSize != S.TentativeDefinitions.size()\n";
- // typedef LazyVector<VarDecl *, ExternalSemaSource,
- // &ExternalSemaSource::ReadTentativeDefinitions, 2, 2>
- // TentativeDefinitionsType;
- assert(CP.TentativeDefinitionsSize == S.TentativeDefinitions.end());
- }
-
- if (CP.ExternalDeclarationsSize != S.ExternalDeclarations.size()) {
- llvm::dbgs()
- << "CP.ExternalDeclarationsSize != S.ExternalDeclarations.size()\n";
- // SmallVector<DeclaratorDecl *, 4> ExternalDeclarations;
- assert(CP.ExternalDeclarationsSize == S.ExternalDeclarations.size());
- }
-
- if (CP.ParsingInitForAutoVarsSize != S.ParsingInitForAutoVars.size()) {
- llvm::dbgs()
- << "CP.ParsingInitForAutoVarsSize != S.ParsingInitForAutoVars.size()\n";
- // llvm::SmallPtrSet<const Decl *, 4> ParsingInitForAutoVars;
- assert(CP.ParsingInitForAutoVarsSize == S.ParsingInitForAutoVars.size());
- }
-
- if (CP.DeclsToCheckForDeferredDiagsSize !=
- S.DeclsToCheckForDeferredDiags.size()) {
- llvm::dbgs() << "CP.DeclsToCheckForDeferredDiagsSize != "
- "S.DeclsToCheckForDeferredDiags.size()\n";
- // llvm::SmallSetVector<Decl *, 4> DeclsToCheckForDeferredDiags;
- assert(CP.DeclsToCheckForDeferredDiagsSize ==
- S.DeclsToCheckForDeferredDiags.size());
- }
-
- if (CP.ShadowingDeclsSize != S.ShadowingDecls.size()) {
- llvm::dbgs() << "CP.ShadowingDeclsSize != S.ShadowingDecls.size()\n";
- // llvm::DenseMap<const NamedDecl *, const NamedDecl *> ShadowingDecls;
- assert(CP.ShadowingDeclsSize == S.ShadowingDecls.size());
- }
-
- if (CP.WeakTopLevelDeclSize != S.WeakTopLevelDecl.size()) {
- llvm::dbgs() << "CP.WeakTopLevelDeclSize != S.WeakTopLevelDecl.size()\n";
- // SmallVector<Decl *, 2> WeakTopLevelDecl;
- assert(CP.WeakTopLevelDeclSize == S.WeakTopLevelDecl.size());
- }
-
- if (CP.ExtVectorDeclsSize != S.ExtVectorDecls.end()) {
- llvm::dbgs() << "CP.ExtVectorDeclsSize != S.ExtVectorDecls.size()\n";
- // typedef LazyVector<TypedefNameDecl *, ExternalSemaSource,
- // &ExternalSemaSource::ReadExtVectorDecls, 2, 2> ExtVectorDecls;
- assert(CP.ExtVectorDeclsSize == S.ExtVectorDecls.end());
- }
-
- if (CP.VTableUsesSize != S.VTableUses.size()) {
- llvm::dbgs() << "CP.VTableUsesSize != S.VTableUses.size()\n";
- // SmallVector<VTableUse, 16> VTableUses;
- assert(CP.VTableUsesSize == S.VTableUses.size());
- }
-
- if (CP.VTablesUsedSize != S.VTablesUsed.size()) {
- llvm::dbgs() << "CP.VTablesUsedSize != S.VTablesUsed.size()\n";
- // llvm::DenseMap<CXXRecordDecl *, bool> VTablesUsed;
- eraseDenseMapIf(
- S.VTablesUsed,
- [&](llvm::detail::DenseMapPair<CXXRecordDecl *, bool> &KV) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(KV.getFirst()), SlabCP);
- });
- // assert(CP.VTablesUsedSize == S.VTablesUsed.size());
- }
-
- if (CP.DelayedDllExportClassesSize != S.DelayedDllExportClasses.size()) {
- llvm::dbgs() << "CP.DelayedDllExportClassesSize != "
- "S.DelayedDllExportClasses.size()\n";
- // SmallVector<CXXRecordDecl *, 4> DelayedDllExportClasses;
- assert(CP.DelayedDllExportClassesSize == S.DelayedDllExportClasses.size());
- }
-
- if (CP.DelayedDllExportMemberFunctionsSize !=
- S.DelayedDllExportMemberFunctions.size()) {
- llvm::dbgs() << "CP.DelayedDllExportMemberFunctionsSize != "
- "S.DelayedDllExportMemberFunctions.size()\n";
- // SmallVector<CXXMethodDecl *, 4> DelayedDllExportMemberFunctions;
- assert(CP.DelayedDllExportMemberFunctionsSize ==
- S.DelayedDllExportMemberFunctions.size());
- }
-
- if (CP.InventedParameterInfosSize != S.InventedParameterInfos.size()) {
- llvm::dbgs()
- << "CP.InventedParameterInfosSize != S.InventedParameterInfos.size()\n";
- // SmallVector<InventedTemplateParameterInfo, 4> InventedParameterInfos;
- assert(CP.InventedParameterInfosSize == S.InventedParameterInfos.size());
- }
-
- if (CP.UnusedPrivateFieldsSize != S.UnusedPrivateFields.size()) {
- llvm::dbgs()
- << "CP.UnusedPrivateFieldsSize != S.UnusedPrivateFields.size()\n";
- // typedef llvm::SmallSetVector<const NamedDecl *, 16> NamedDeclSetType;
- /// Set containing all declared private fields that are not used.
- // NamedDeclSetType UnusedPrivateFields;
- assert(CP.UnusedPrivateFieldsSize == S.UnusedPrivateFields.size());
- }
-
- if (S.PureVirtualClassDiagSet &&
- (CP.PureVirtualClassDiagSetSize != S.PureVirtualClassDiagSet->size())) {
- llvm::dbgs() << "CP.PureVirtualClassDiagSetSize != "
- "S.PureVirtualClassDiagSet.size()\n";
- // typedef llvm::SmallPtrSet<const CXXRecordDecl *, 8> RecordDeclSetTy;
-
- /// PureVirtualClassDiagSet - a set of class declarations which we have
- /// emitted a list of pure virtual functions. Used to prevent emitting the
- /// same list more than once.
- // std::unique_ptr<RecordDeclSetTy> PureVirtualClassDiagSet;
- eraseSmallPtrSetIf(
- *S.PureVirtualClassDiagSet.get(), [&](const CXXRecordDecl *RD) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(RD, SlabCP);
- });
- // assert(CP.PureVirtualClassDiagSetSize ==
- // S.PureVirtualClassDiagSet->size());
- }
-
- if (CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.end()) {
- llvm::dbgs()
- << "CP.DelegatingCtorDeclsSize != S.DelegatingCtorDecls.size()\n";
- // typedef LazyVector<CXXConstructorDecl *, ExternalSemaSource,
- // &ExternalSemaSource::ReadDelegatingConstructors, 2, 2>
- // DelegatingCtorDeclsType;
-
- /// All the delegating constructors seen so far in the file, used for
- /// cycle detection at the end of the TU.
- // DelegatingCtorDeclsType DelegatingCtorDecls;
-
- assert(CP.DelegatingCtorDeclsSize == S.DelegatingCtorDecls.end());
- }
-
- if (CP.UnparsedDefaultArgLocsSize != S.UnparsedDefaultArgLocs.size()) {
- llvm::dbgs()
- << "CP.UnparsedDefaultArgLocsSize != S.UnparsedDefaultArgLocs.size()\n";
- // llvm::DenseMap<ParmVarDecl *, SourceLocation> UnparsedDefaultArgLocs;
- assert(CP.UnparsedDefaultArgLocsSize == S.UnparsedDefaultArgLocs.size());
- }
-
- if (CP.UndefinedButUsedSize != S.UndefinedButUsed.size()) {
- llvm::dbgs() << "CP.UndefinedButUsedSize != S.UndefinedButUsed.size()\n";
- // llvm::MapVector<NamedDecl *, SourceLocation> UndefinedButUsed;
- while (CP.UndefinedButUsedSize != S.UndefinedButUsed.size())
- S.UndefinedButUsed.pop_back();
- assert(CP.UndefinedButUsedSize == S.UndefinedButUsed.size());
- }
-
- if (CP.SpecialMembersBeingDeclaredSize !=
- S.SpecialMembersBeingDeclared.size()) {
- llvm::dbgs() << "CP.SpecialMembersBeingDeclaredSize != "
- "S.SpecialMembersBeingDeclared.size()\n";
- // llvm::SmallPtrSet<SpecialMemberDecl, 4> SpecialMembersBeingDeclared;
- assert(CP.SpecialMembersBeingDeclaredSize ==
- S.SpecialMembersBeingDeclared.size());
- }
-
- if (CP.SpecialMembersBeingDeclaredSize !=
- S.SpecialMembersBeingDeclared.size()) {
- llvm::dbgs() << "CP.SpecialMembersBeingDeclaredSize != "
- "S.SpecialMembersBeingDeclared.size()\n";
- // llvm::SmallPtrSet<SpecialMemberDecl, 4> SpecialMembersBeingDeclared;
- assert(CP.SpecialMembersBeingDeclaredSize ==
- S.SpecialMembersBeingDeclared.size());
- }
-
- if (CP.DelayedOverridingExceptionSpecChecksSize !=
- S.DelayedOverridingExceptionSpecChecks.size()) {
- llvm::dbgs() << "CP.DelayedOverridingExceptionSpecChecksSize != "
- "S.DelayedOverridingExceptionSpecChecks.size()\n";
- // SmallVector<std::pair<const CXXMethodDecl *, const CXXMethodDecl *>,
- // 2>
- // DelayedOverridingExceptionSpecChecks;
- assert(CP.DelayedOverridingExceptionSpecChecksSize ==
- S.DelayedOverridingExceptionSpecChecks.size());
- }
-
- if (CP.DelayedEquivalentExceptionSpecChecksSize !=
- S.DelayedEquivalentExceptionSpecChecks.size()) {
- llvm::dbgs() << "CP.DelayedEquivalentExceptionSpecChecksSize != "
- "S.DelayedEquivalentExceptionSpecChecks.size()\n";
- // SmallVector<std::pair<FunctionDecl *, FunctionDecl *>, 2>
- // DelayedEquivalentExceptionSpecChecks;
- assert(CP.DelayedEquivalentExceptionSpecChecksSize ==
- S.DelayedEquivalentExceptionSpecChecks.size());
- }
-
- if (CP.MaybeODRUseExprsSize != S.MaybeODRUseExprs.size()) {
- llvm::dbgs() << "CP.MaybeODRUseExprsSize != S.MaybeODRUseExprs.size()\n";
- // S.MaybeODRUseExprs.resize(CP.MaybeODRUseExprsSize);
- assert(CP.MaybeODRUseExprsSize == S.MaybeODRUseExprs.size());
- }
-
- if (CP.RefsMinusAssignmentsSize != S.RefsMinusAssignments.size()) {
- llvm::dbgs()
- << "CP.RefsMinusAssignmentsSize != S.RefsMinusAssignments.size()\n";
- // llvm::DenseMap<const VarDecl *, int> RefsMinusAssignments;
- eraseDenseMapIf(
- S.RefsMinusAssignments,
- [&](llvm::detail::DenseMapPair<const VarDecl *, int> &KV) -> bool {
- return Ctx.getAllocator().isAfterCheckpoint(
- static_cast<void *>(const_cast<VarDecl *>(KV.getFirst())),
- SlabCP);
- });
- assert(CP.RefsMinusAssignmentsSize == S.RefsMinusAssignments.size());
- }
-
- if (CP.ExprCleanupObjectsSize != S.ExprCleanupObjects.size()) {
- llvm::dbgs()
- << "CP.ExprCleanupObjectsSize != S.ExprCleanupObjects.size()\n";
- // SmallVector<ExprWithCleanups::CleanupObject, 8> ExprCleanupObjects;
- assert(CP.ExprCleanupObjectsSize == S.ExprCleanupObjects.size());
- }
-
- if (CP.ExprEvalContextsSize != S.ExprEvalContexts.size()) {
- llvm::dbgs() << "CP.ExprEvalContextsSize != S.ExprEvalContexts.size()\n";
- // SmallVector<ExpressionEvaluationContextRecord, 8> ExprEvalContexts;
- assert(CP.ExprEvalContextsSize == S.ExprEvalContexts.size());
- }
-
- if (CP.FailedImmediateInvocationsSize !=
- S.FailedImmediateInvocations.size()) {
- llvm::dbgs() << "CP.FailedImmediateInvocationsSize != "
- "S.FailedImmediateInvocations.size()\n";
- // llvm::SmallPtrSet<ConstantExpr *, 4> FailedImmediateInvocations;
- assert(CP.FailedImmediateInvocationsSize ==
- S.FailedImmediateInvocations.size());
- }
-
- if (CP.DeleteExprsSize != S.DeleteExprs.size()) {
- llvm::dbgs() << "CP.DeleteExprsSize != S.DeleteExprs.size()\n";
- // llvm::MapVector<FieldDecl *, DeleteLocs> DeleteExprs;
- assert(CP.DeleteExprsSize == S.DeleteExprs.size());
- }
-
- if (CP.CurrentParameterCopyTypesSize != S.CurrentParameterCopyTypes.size()) {
- llvm::dbgs() << "CP.CurrentParameterCopyTypesSize != "
- "S.CurrentParameterCopyTypes.size()\n";
- // llvm::SmallVector<QualType, 4> CurrentParameterCopyTypes;
- assert(CP.CurrentParameterCopyTypesSize ==
- S.CurrentParameterCopyTypes.size());
- }
-
- if (CP.AggregateDeductionCandidatesSize !=
- S.AggregateDeductionCandidates.size()) {
- llvm::dbgs() << "CP.AggregateDeductionCandidatesSize != "
- "S.AggregateDeductionCandidates.size()\n";
- // llvm::DenseMap<unsigned, CXXDeductionGuideDecl *>
- // AggregateDeductionCandidates;
- assert(CP.AggregateDeductionCandidatesSize ==
- S.AggregateDeductionCandidates.size());
- }
-
- if (CP.TypoCorrectionFailuresSize != S.TypoCorrectionFailures.size()) {
- llvm::dbgs() << "CP.TypoCorrectionFailuresSize != "
- "S.TypoCorrectionFailures.size()\n";
- // eraseDenseMapIf(
- // S.TypoCorrectionFailures,
- // [&](llvm::detail::DenseMapPair<IdentifierInfo *, Sema::SrcLocSet>
- // &KV)
- // -> bool {
- // llvm::outs() << "SlabCheckPoint Cur : " << (void
- // *)SlabCP.CurPtr << "\n"; llvm::outs() << "SlabCheckPoint END :
- // " << (void *)SlabCP.End << "\n"; llvm::outs() << "Addr: " <<
- // static_cast<void *>(KV.getFirst()) << "\n";
- // return Ctx.getAllocator().isAfterCheckpoint(
- // static_cast<void *>(KV.getFirst()), SlabCP);
- // });
- // assert(CP.TypoCorrectionFailuresSize == S.TypoCorrectionFailures.size());
- }
-
- if (CP.ModuleScopesSize != S.ModuleScopes.size()) {
- llvm::dbgs() << "CP.ModuleScopesSize != "
- "S.ModuleScopes.size()\n";
- // llvm::SmallVector<ModuleScope, 16> ModuleScopes;
- assert(CP.ModuleScopesSize == S.ModuleScopes.size());
- }
-
- if (CP.DeferredExportedNamespacesSize !=
- S.DeferredExportedNamespaces.size()) {
- llvm::dbgs() << "CP.DeferredExportedNamespacesSize != "
- "S.DeferredExportedNamespaces.size()\n";
- /// Namespace definitions that we will export when they finish.
- // llvm::SmallPtrSet<const NamespaceDecl *, 8> DeferredExportedNamespaces;
- assert(CP.DeferredExportedNamespacesSize ==
- S.DeferredExportedNamespaces.size());
- }
-
- if (CP.PendingInlineFuncDeclsSize != S.PendingInlineFuncDecls.size()) {
- llvm::dbgs() << "CP.PendingInlineFuncDeclsSize != "
- "S.PendingInlineFuncDecls.size()\n";
- /// /// In a C++ standard module, inline declarations require a definition
- /// to be
- /// present at the end of a definition domain. This set holds the decls to
- /// be checked at the end of the TU.
- // llvm::SmallPtrSet<const FunctionDecl *, 8> PendingInlineFuncDecls;
-
- assert(CP.PendingInlineFuncDeclsSize == S.PendingInlineFuncDecls.size());
- }
-
- if (CP.CurrentSEHFinallySize != S.CurrentSEHFinally.size()) {
- llvm::dbgs() << "CP.CurrentSEHFinallySize != "
- "S.CurrentSEHFinally.size()\n";
- // /// Stack of active SEH __finally scopes. Can be empty.
- // SmallVector<Scope *, 2> CurrentSEHFinally;
-
- assert(CP.CurrentSEHFinallySize == S.CurrentSEHFinally.size());
- }
-
- if (CP.CurrentDeferSize != S.CurrentDefer.size()) {
- llvm::dbgs() << "CP.CurrentDeferSize != "
- "S.CurrentDefer.size()\n";
- // /// Stack of '_Defer' statements that are currently being parsed, as
- // well
- /// as the locations of their '_Defer' keywords. Can be empty.
- // SmallVector<std::pair<Scope *, SourceLocation>, 2> CurrentDefer;
-
- assert(CP.CurrentDeferSize == S.CurrentDefer.size());
- }
-
- if (CP.LateParsedTemplateMapSize != S.LateParsedTemplateMap.size()) {
- llvm::dbgs() << "CP.LateParsedTemplateMapSize != "
- "S.LateParsedTemplateMap.size()\n";
- // typedef llvm::MapVector<const FunctionDecl *,
- // std::unique_ptr<LateParsedTemplate>>
- // LateParsedTemplateMapT;
- // LateParsedTemplateMapT LateParsedTemplateMap;
- assert(CP.LateParsedTemplateMapSize == S.LateParsedTemplateMap.size());
- }
-
- if (CP.SuppressedDiagnosticsSize != S.SuppressedDiagnostics.size()) {
- llvm::dbgs() << "CP.SuppressedDiagnosticsSize != "
- "S.SuppressedDiagnostics.size()\n";
- // typedef llvm::DenseMap<Decl *, SmallVector<PartialDiagnosticAt, 1>>
- // SuppressedDiagnosticsMap;
- // SuppressedDiagnosticsMap SuppressedDiagnostics;
- assert(CP.SuppressedDiagnosticsSize == S.SuppressedDiagnostics.size());
- }
-
- if (CP.UnparsedDefaultArgInstantiationsSize !=
- S.UnparsedDefaultArgInstantiations.size()) {
- llvm::dbgs() << "CP.UnparsedDefaultArgInstantiationsSize != "
- "S.UnparsedDefaultArgInstantiations.size()\n";
-
- // typedef llvm::DenseMap<ParmVarDecl *, llvm::TinyPtrVector<ParmVarDecl
- // *>>
- // UnparsedDefaultArgInstantiationsMap;
-
- // /// A mapping from parameters with unparsed default arguments to the
- // /// set of instantiations of each parameter.
- // ///
- // /// This mapping is a temporary data structure used when parsing
- // /// nested class templates or nested classes of class templates,
- // /// where we might end up instantiating an inner class before the
- // /// default arguments of its methods have been parsed.
- // UnparsedDefaultArgInstantiationsMap UnparsedDefaultArgInstantiations;
-
- assert(CP.UnparsedDefaultArgInstantiationsSize ==
- S.UnparsedDefaultArgInstantiations.size());
- }
-
- if (CP.CodeSynthesisContextsSize != S.CodeSynthesisContexts.size()) {
- llvm::dbgs() << "CP.CodeSynthesisContextsSize != "
- "S.CodeSynthesisContexts.size()\n";
- // SmallVector<CodeSynthesisContext, 16> CodeSynthesisContexts;
-
- assert(CP.CodeSynthesisContextsSize == S.CodeSynthesisContexts.size());
- }
-
- if (CP.InstantiatingSpecializationsSize !=
- S.InstantiatingSpecializations.size()) {
- llvm::dbgs() << "CP.InstantiatingSpecializationsSize != "
- "S.InstantiatingSpecializations.size()\n";
- // /// Specializations whose definitions are currently being
- // instantiated.
- // llvm::DenseSet<InstantiatingSpecializationsKey>
- // InstantiatingSpecializations;
-
- assert(CP.InstantiatingSpecializationsSize ==
- S.InstantiatingSpecializations.size());
- }
-
- if (CP.InstantiatedNonDependentTypesSize !=
- S.InstantiatedNonDependentTypes.size()) {
- llvm::dbgs() << "CP.InstantiatedNonDependentTypesSize != "
- "S.InstantiatedNonDependentTypes.size()\n";
- // llvm::DenseSet<QualType> InstantiatedNonDependentTypes;
- assert(CP.InstantiatedNonDependentTypesSize ==
- S.InstantiatedNonDependentTypes.size());
- }
-
- if (CP.CodeSynthesisContextLookupModulesSize !=
- S.CodeSynthesisContextLookupModules.size()) {
- llvm::dbgs() << "CP.CodeSynthesisContextLookupModulesSize != "
- "S.CodeSynthesisContextLookupModules.size()\n";
- // SmallVector<Module *, 16> CodeSynthesisContextLookupModules;
-
- assert(CP.CodeSynthesisContextLookupModulesSize ==
- S.CodeSynthesisContextLookupModules.size());
- }
-
- if (CP.LookupModulesCacheSize != S.LookupModulesCache.size()) {
- llvm::dbgs() << "CP.LookupModulesCacheSize != "
- "S.LookupModulesCache.size()\n";
- // llvm::DenseSet<Module *> LookupModulesCache;
-
- assert(CP.LookupModulesCacheSize == S.LookupModulesCache.size());
- }
-
- if (CP.VisibleNamespaceCacheSize != S.VisibleNamespaceCache.size()) {
- llvm::dbgs() << "CP.VisibleNamespaceCacheSize != "
- "S.VisibleNamespaceCache.size()\n";
- // llvm::DenseMap<NamedDecl *, NamedDecl *> VisibleNamespaceCache;
-
- assert(CP.VisibleNamespaceCacheSize == S.VisibleNamespaceCache.size());
- }
-
- // if (CP.TemplateInstCallbacksSize != S.TemplateInstCallbacks.size()) {
- // llvm::dbgs() << "CP.TemplateInstCallbacksSize != "
- // "S.TemplateInstCallbacks.size()\n";
- // // std::vector<std::unique_ptr<TemplateInstantiationCallback>>
- // // TemplateInstCallbacks;
- // assert(CP.TemplateInstCallbacksSize == S.TemplateInstCallbacks.size());
- // }
-
- if (CP.PendingInstantiationsSize != S.PendingInstantiations.size()) {
- llvm::dbgs() << "CP.PendingInstantiationsSize != "
- "S.PendingInstantiations.size()\n";
- // std::deque<PendingImplicitInstantiation> PendingInstantiations;
-
- assert(CP.PendingInstantiationsSize == S.PendingInstantiations.size());
- }
-
- if (CP.LateParsedInstantiationsSize != S.LateParsedInstantiations.size()) {
- llvm::dbgs() << "CP.LateParsedInstantiationsSize != "
- "S.LateParsedInstantiations.size()\n";
- S.LateParsedInstantiations.resize(CP.LateParsedInstantiationsSize);
- assert(CP.LateParsedInstantiationsSize ==
- S.LateParsedInstantiations.size());
- }
-
- if (CP.SavedVTableUsesSize != S.SavedVTableUses.size()) {
- llvm::dbgs() << "CP.SavedVTableUsesSize != "
- "S.SavedVTableUses.size()\n";
- S.SavedVTableUses.resize(CP.SavedVTableUsesSize);
- assert(CP.SavedVTableUsesSize == S.SavedVTableUses.size());
- }
-
- if (CP.SavedPendingInstantiationsSize !=
- S.SavedPendingInstantiations.size()) {
- llvm::dbgs() << "CP.SavedPendingInstantiationsSize != "
- "S.SavedPendingInstantiations.size()\n";
- S.SavedPendingInstantiations.resize(CP.SavedPendingInstantiationsSize);
- assert(CP.SavedPendingInstantiationsSize ==
- S.SavedPendingInstantiations.size());
- }
-
- if (CP.PendingLocalImplicitInstantiationsSize !=
- S.PendingLocalImplicitInstantiations.size()) {
- llvm::dbgs() << "CP.PendingLocalImplicitInstantiationsSize != "
- "S.PendingLocalImplicitInstantiations.size()\n";
- S.PendingLocalImplicitInstantiations.resize(
- CP.PendingLocalImplicitInstantiationsSize);
- assert(CP.PendingLocalImplicitInstantiationsSize ==
- S.PendingLocalImplicitInstantiations.size());
- }
-
- if (S.CurrentCachedTemplateArgs && (CP.CurrentCachedTemplateArgsSize !=
- S.CurrentCachedTemplateArgs->size())) {
- llvm::dbgs() << "CP.CurrentCachedTemplateArgsSize != "
- "S.CurrentCachedTemplateArgs->size()\n ";
- // eraseDenseMapIf(
- // S.UnsubstitutedConstraintSatisfactionCache,
- // [&](UnsubstitutedConstraintSatisfactionCacheResult
- // &R) -> bool {
- // return Ctx.isAfterCheckpoint(static_cast<void
- // *>(R.SubstExpr.get()),
- // SlabCP)
- // });
- assert(CP.CurrentCachedTemplateArgsSize ==
- S.CurrentCachedTemplateArgs->size());
- }
-
- if (CP.UnsubstitutedConstraintSatisfactionCacheSize !=
- S.UnsubstitutedConstraintSatisfactionCache.size()) {
- llvm::dbgs() << "CP.UnsubstitutedConstraintSatisfactionCacheSize != "
- "S.UnsubstitutedConstraintSatisfactionCache.size()\n";
- // eraseDenseMapIf(
- // S.UnsubstitutedConstraintSatisfactionCache,
- // [&](UnsubstitutedConstraintSatisfactionCacheResult &R) -> bool {
- // return Ctx.isAfterCheckpoint(static_cast<void
- // *>(R.SubstExpr.get()),
- // SlabCP)
- // });
- assert(CP.UnsubstitutedConstraintSatisfactionCacheSize ==
- S.UnsubstitutedConstraintSatisfactionCache.size());
- }
-
- if (CP.SubsumptionCacheSize != S.SubsumptionCache.size()) {
- llvm::dbgs() << "CP.SubsumptionCacheSize != "
- "S.SubsumptionCache.size()\n";
- // eraseDenseMapIf(
- // S.SubsumptionCache,
- // [&](llvm::DenseMap<std::pair<const NamedDecl *, const NamedDecl *>,
- // bool>::key_value &KV) -> bool {
- // return Ctx.isAfterCheckpoint(static_cast<void *>(KV.first.first),
- // SlabCP) ||
- // Ctx.isAfterCheckpoint(static_cast<void *>(KV.first.second),
- // SlabCP);
- // });
- assert(CP.SubsumptionCacheSize == S.SubsumptionCache.size());
- }
-
- if (CP.NormalizationCacheSize != S.NormalizationCache.size()) {
- llvm::dbgs() << "CP.NormalizationCacheSize != "
- "S.NormalizationCache.size()\n";
- // llvm::DenseMap<ConstrainedDeclOrNestedRequirement, NormalizedConstraint
- // *> NormalizationCache;
- assert(CP.NormalizationCacheSize == S.NormalizationCache.size());
- }
-
- if (CP.SatisfactionCacheSize != S.SatisfactionCache.size()) {
- llvm::dbgs() << "CP.SatisfactionCacheSize != "
- "S.SatisfactionCache.size()\n";
- // llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &>
- // SatisfactionCache;
- assert(CP.SatisfactionCacheSize == S.SatisfactionCache.size());
- }
-
- if (CP.SatisfactionStackSize != S.SatisfactionStack.size()) {
- llvm::dbgs() << "CP.SatisfactionStackSize != "
- "S.SatisfactionStack.size()\n";
- // llvm::SmallVector<SatisfactionStackEntryTy, 10>
- // SatisfactionStack;
- assert(CP.SatisfactionStackSize == S.SatisfactionStack.size());
- }
-
- // if (CP.NullabilityMapSize != S.NullabilityMap.size()) {
- // llvm::dbgs() << "CP.NullabilityMapSize != "
- // "S.NullabilityMap.size()\n";
- // // FileNullabilityMap NullabilityMap;;
- // assert(CP.NullabilityMapSize == S.NullabilityMap.size());
- // }
-
- if (CP.DeclsWithEffectsToVerifySize != S.DeclsWithEffectsToVerify.size()) {
- llvm::dbgs() << "CP.DeclsWithEffectsToVerifySize != "
- "S.DeclsWithEffectsToVerify.size()\n";
- // SmallVector<const Decl *> DeclsWithEffectsToVerify;;
- assert(CP.DeclsWithEffectsToVerifySize ==
- S.DeclsWithEffectsToVerify.size());
- }
-
- if (CP.SpecialMemberCacheSize != S.SpecialMemberCache.size()) {
- llvm::dbgs() << "CP.SpecialMemberCacheSize != "
- "S.SpecialMemberCache.size()\n";
-
- eraseDenseMapIf(
- S.SpecialMemberCache,
- [&](llvm::detail::DenseMapPair<Sema::SpecialMemberCacheKey,
- Sema::SpecialMemberOverloadResult> &KV)
- -> bool {
- return S.BumpAlloc.isAfterCheckpoint(
- static_cast<void *>(KV.getSecond().getMethod()),
- CP.SemaBumpSlabCP);
- });
- assert(CP.SpecialMemberCacheSize == S.SpecialMemberCache.size());
- }
-
- std::vector<NamedDecl *> ToRemoved;
- for (auto *TmpD : S.getCurScope()->decls()) {
- assert(TmpD && "This decl didn't get pushed??");
-
- assert(isa<NamedDecl>(TmpD) && "Decl isn't NamedDecl?");
- NamedDecl *D = cast<NamedDecl>(TmpD);
-
- if (!D->getDeclName())
- continue;
-
- if (Ctx.getAllocator().isAfterCheckpoint(static_cast<void *>(D), SlabCP)) {
- if (D->getDeclName().getFETokenInfo())
- S.IdResolver.RemoveDecl(D);
- ToRemoved.push_back(D);
- }
- }
-
- for (auto ND : ToRemoved)
- S.getCurScope()->RemoveDecl(ND);
-
- S.BumpAlloc.restoreToCheckPoint(CP.SemaBumpSlabCP);
-}
-
-} // namespace clang
>From 58155d0d14ba81b0b32261640f2c92f9ad8c0f7f Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 16 Sep 2026 12:28:19 +0530
Subject: [PATCH 05/13] Refactor code: remove spaces
---
clang/include/clang/AST/ASTContext.h | 1 -
clang/include/clang/AST/DeclTemplate.h | 1 -
clang/lib/Interpreter/IncrementalParser.h | 3 ---
3 files changed, 5 deletions(-)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index e3d09c8257d83..72e6bcf362aeb 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -240,7 +240,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
friend class NestedNameSpecifier;
mutable SmallVector<Type *, 0> Types;
-
mutable llvm::FoldingSet<ExtQuals> ExtQualNodes;
mutable llvm::UniquingSet<ComplexType> ComplexTypes;
mutable llvm::UniquingSet<PointerType> PointerTypes{GeneralTypesLog2InitSize};
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index fd1c7df6e7813..d32b044ef40e2 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -825,7 +825,6 @@ class RedeclarableTemplateDecl : public TemplateDecl,
friend class ASTDeclReader;
friend class ASTDeclWriter;
friend class ASTReader;
-
template <class decl_type> friend class RedeclarableTemplate;
/// Retrieves the canonical declaration of this template.
diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h
index e6eaa7dddb7fd..b626cebaafcd7 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -63,9 +63,6 @@ class IncrementalParser {
void CleanUpPTU(TranslationUnitDecl *MostRecentTU);
- template <typename decl_type>
- void RepairRedeclChain(decl_type *D, TranslationUnitDecl *PTU);
-
/// Register a PTU produced by Parse.
PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU,
std::unique_ptr<llvm::Module> M = {});
>From cb80e9d3b67efbbfd393715609dd2c54c641335d Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Mon, 21 Sep 2026 14:32:39 +0530
Subject: [PATCH 06/13] Refactor code
---
.../include/clang/Interpreter/ErrorRecovery.h | 300 ++++++++++--------
1 file changed, 167 insertions(+), 133 deletions(-)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 2ef75b2492fa2..7421f28110955 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -17,9 +17,6 @@
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
-#include "clang/Sema/Sema.h"
-
-#include "llvm/ADT/DenseMap.h"
namespace clang {
class ASTContext;
@@ -562,17 +559,13 @@ template <typename DataT> class StateAwareChain {
return nullptr;
}
- /// Pure removal: drop every entry with ID >= \p ID (LIFO). Never
- /// restores anything -- callers must read mostRecent()/getPrevious()
- /// themselves first if they need the value about to be dropped.
+ /// removal: drop every entry with ID >= \p ID.
void removeFrom(PTUID ID) {
while (!History.empty() && History.back().ID >= ID)
History.pop_back(); // no delete.
}
};
-// struct UnusedTrait {};
-
using RecordDeclDefinitionDataChain = StateAwareChain<DefinitionDataFootprint>;
using SpecializationChain = StateAwareChain<SpecializationFootprint>;
using VarSpecializationChain = StateAwareChain<VarSpecializationFootprint>;
@@ -630,6 +623,7 @@ class PTUCheckpointLedger {
}
bool isFromThisPTU(const ASTContext &Ctx, const void *Ptr, PTUID ID) const {
+ assert(ID < CheckpointBeforePTU.size());
return Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
}
@@ -701,6 +695,22 @@ template <typename OwnerT, typename ValueT> class FieldMutationChain {
Log.erase(O);
}
+ /// Same trim-then-erase-if-empty as removeFrom(ID), scoped to one owner
+ /// instead of sweeping the whole Log -- for callers that already know
+ /// exactly which owners this PTU touched (see PTUStateInfo's
+ /// TouchedFunctionTypeOwners/TouchedTagdeclOwners) and don't need to
+ /// rediscover that by walking every owner ever logged.
+ void removeFromOwner(const OwnerT *Owner, PTUID ID) {
+ auto It = Log.find(Owner);
+ if (It == Log.end())
+ return;
+ auto &Entries = It->second;
+ while (!Entries.empty() && Entries.back().ID >= ID)
+ Entries.pop_back();
+ if (Entries.empty())
+ Log.erase(It);
+ }
+
void forget(const OwnerT *Owner) { Log.erase(Owner); }
};
@@ -763,19 +773,39 @@ struct PTUStateInfo {
/// here touched info mean other this belongs to other PTUs;
llvm::SmallPtrSet<const DeclContext *, 4> TouchedDC;
+ /// Exactly the decls this PTU's own commit() pushed a footprint-chain
+ /// entry for (DefinitionData/Specialization/MemberSpecialization, via
+ /// chainFor()/memberSpecChainFor()), keyed on the canonical decl the
+ /// same way those accessors are. Lets undo walk only what this PTU
+ /// actually touched instead of sweeping every decl ever tracked across
+ /// every PTU -- the exact opposite of commit(), scoped the same way
+ /// commit() itself is scoped.
+ llvm::SmallPtrSet<const Decl *, 8> TouchedFootprintDecls;
+
+ /// Same idea for the two FieldMutationChain logs (FunctionTypeMutations,
+ /// TagdeclInfos), which are keyed by owner rather than living in
+ /// LinkedDecls -- kept as two separate sets since the owner types
+ /// (FunctionDecl vs TagDecl) differ.
+ llvm::SmallPtrSet<const FunctionDecl *, 4> TouchedFunctionTypeOwners;
+ llvm::SmallPtrSet<const TagDecl *, 4> TouchedTagdeclOwners;
+
+ explicit PTUStateInfo(PTUID ID) : ID(ID) {}
+
template <typename KindT>
void noteMutated(const Decl *D, DeclShape S, KindT K) {
if (!D->isDefinedOutsideFunctionOrMethod())
return;
auto [It, Inserted] = Mutations.try_emplace(D);
if (Inserted) {
- assert(It->second.S == S && "same decl noted under two different shapes");
It->second.S = S;
}
+
+ assert(It->second.S == S && "same decl noted under two different shapes");
+
It->second.add(K);
}
- void verifyMutations();
+ void verifyMutations(PTUMutationActions &Actions);
// False until this PTU is actually committed.
// PTUMutationActions uses this to decide what "undo" should do:
@@ -822,11 +852,10 @@ class SweepTracker {
Active.erase(It);
}
- // Call at commit(ID) time. OnConfirmed is invoked as
+ // Call at commit(ID) time. PTUMutationActions is invoked as
// (const Decl *D, DeclShape S, uint32_t ConfirmedKinds) for every decl
// that had something newly confirmed this sweep.
- template <typename OnConfirmedFn>
- void sweep(PTUID ID, OnConfirmedFn &&OnConfirmed);
+ void sweep(PTUMutationActions &Act);
};
//===----------------------------------------------------------------------===//
@@ -1028,10 +1057,9 @@ class IncrementalStateTracker {
ASTContext &Ctx;
LangMode Mode;
PTUID NextID = 0;
- PTUID CurID = NextID;
PTUCheckpointLedger PTUSlabCheckpoints;
- mutable llvm::DenseMap<PTUID, PTUStateInfo> PTUStateInfos;
+ mutable std::vector<PTUStateInfo> PTUStack;
// Stores all declaration footprint state in one map instead of maintaining
// separate maps for each declaration/specialization kind. The generator
@@ -1048,77 +1076,88 @@ class IncrementalStateTracker {
friend class PTUMutationActions;
- SweepTracker &getHiddenMutationTracker() { return HiddenMutationTracker; }
+ void noteFunctionTypeMutation(PTUID ID, const FunctionDecl *Owner,
+ QualType OldValue) {
+ FunctionTypeMutations.noteMutation(ID, Owner, OldValue);
+ current().TouchedFunctionTypeOwners.insert(Owner);
+ }
+ void noteTagDeclTypeMutation(PTUID ID, const TagDecl *Owner,
+ const Type *OldValue) {
+ TagdeclInfos.noteMutation(ID, Owner, OldValue);
+ current().TouchedTagdeclOwners.insert(Owner);
+ }
/// Get-or-create the CXXClassDeclNode backing Canon, acquiring one from
/// the generator on first use. The only place a CXXClassDeclNode is
/// created for this map.
- CXXClassDeclNode &recordNodeFor(const CXXRecordDecl *Canon) const {
- DeclLinkedState &Info = LinkedDecls[Canon];
+ template <typename NodeT, typename AcquireFn>
+ NodeT &nodeFor(const Decl *D, AcquireFn Acquire) {
+ current().TouchedFootprintDecls.insert(D);
+ DeclLinkedState &Info = LinkedDecls[D];
if (Info.isNull())
- Info = Generator.acquireRecordNode();
- return *cast<CXXClassDeclNode *>(Info);
+ Info = (Generator.*Acquire)();
+ return *cast<NodeT *>(Info);
}
- VarDeclNode &varNodeFor(const VarDecl *Canon) const {
- DeclLinkedState &Info = LinkedDecls[Canon];
- if (Info.isNull())
- Info = Generator.acquireVarNode();
- return *cast<VarDeclNode *>(Info);
+
+ template <typename NodeT> NodeT *lookupNode(const Decl *D) const {
+ auto It = LinkedDecls.find(D);
+ if (It == LinkedDecls.end() || It->second.isNull())
+ return nullptr;
+ return dyn_cast<NodeT *>(It->second);
}
- FunctionDeclNode &functionNodeFor(const FunctionDecl *Canon) const {
- DeclLinkedState &Info = LinkedDecls[Canon];
- if (Info.isNull())
- Info = Generator.acquireFunctionNode();
- return *cast<FunctionDeclNode *>(Info);
+
+ CXXClassDeclNode &recordNodeFor(const CXXRecordDecl *RD) {
+ return nodeFor<CXXClassDeclNode>(
+ RD, &LinkedDeclNodeGenerator::acquireRecordNode);
}
- EnumDeclNode &enumNodeFor(const EnumDecl *Canon) const {
- DeclLinkedState &Info = LinkedDecls[Canon];
- if (Info.isNull())
- Info = Generator.acquireEnumNode();
- return *cast<EnumDeclNode *>(Info);
+ VarDeclNode &varNodeFor(const VarDecl *VD) {
+ return nodeFor<VarDeclNode>(VD, &LinkedDeclNodeGenerator::acquireVarNode);
+ }
+ FunctionDeclNode &functionNodeFor(const FunctionDecl *FD) {
+ return nodeFor<FunctionDeclNode>(
+ FD, &LinkedDeclNodeGenerator::acquireFunctionNode);
+ }
+ EnumDeclNode &enumNodeFor(const EnumDecl *ED) {
+ return nodeFor<EnumDeclNode>(ED, &LinkedDeclNodeGenerator::acquireEnumNode);
+ }
+
+ template <typename WantT, typename NodeT, typename AcquireFn>
+ WantT &specChain(NodeT &Node, AcquireFn Acquire) {
+ if (auto *C = Node.Spec.template dyn_cast<WantT *>())
+ return *C;
+ assert(Node.Spec.isNull() && "node already holds the other Spec kind");
+ auto *C = (Generator.*Acquire)();
+ Node.Spec = C;
+ return *C;
}
RecordDeclDefinitionDataChain &chainFor(const CXXRecordDecl *RD) {
- CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+ CXXClassDeclNode &Node = recordNodeFor(RD);
if (!Node.DefData)
Node.DefData = Generator.acquireDefDataChain();
return *Node.DefData;
}
- SpecializationChain &chainFor(const ClassTemplateSpecializationDecl *Spec) {
- CXXClassDeclNode &Node = recordNodeFor(
- cast<ClassTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
- auto *Chain = Node.Spec.dyn_cast<SpecializationChain *>();
- if (!Chain) {
- Chain = Generator.acquireClassSpecChain();
- Node.Spec = Chain;
- }
- return *Chain;
+ SpecializationChain &chainFor(const ClassTemplateSpecializationDecl *S) {
+ return specChain<SpecializationChain>(
+ recordNodeFor(cast<CXXRecordDecl>(S)),
+ &LinkedDeclNodeGenerator::acquireClassSpecChain);
}
- VarSpecializationChain &chainFor(const VarTemplateSpecializationDecl *Spec) {
- VarDeclNode &Node = varNodeFor(
- cast<VarTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
- auto *Chain = Node.Spec.dyn_cast<VarSpecializationChain *>();
- if (!Chain) {
- Chain = Generator.acquireVarSpecChain();
- Node.Spec = Chain;
- }
- return *Chain;
+ VarSpecializationChain &chainFor(const VarTemplateSpecializationDecl *S) {
+ return specChain<VarSpecializationChain>(
+ varNodeFor(cast<VarDecl>(S)),
+ &LinkedDeclNodeGenerator::acquireVarSpecChain);
}
- FunctionSpecializationChain &chainFor(const FunctionDecl *FD) {
- FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
- auto *Chain = Node.Spec.dyn_cast<FunctionSpecializationChain *>();
- if (!Chain) {
- Chain = Generator.acquireFunctionSpecChain();
- Node.Spec = Chain;
- }
- return *Chain;
+ FunctionSpecializationChain &chainFor(const FunctionDecl *S) {
+ return specChain<FunctionSpecializationChain>(
+ functionNodeFor(cast<FunctionDecl>(S)),
+ &LinkedDeclNodeGenerator::acquireFunctionSpecChain);
}
MemberSpecializationChain &memberSpecChainFor(const FunctionDecl *FD) {
- FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
+ FunctionDeclNode &Node = functionNodeFor(FD);
auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
if (!Chain) {
Chain = Generator.acquireMemberSpecChain();
@@ -1127,7 +1166,7 @@ class IncrementalStateTracker {
return *Chain;
}
MemberSpecializationChain &memberSpecChainFor(const VarDecl *VD) {
- VarDeclNode &Node = varNodeFor(VD->getCanonicalDecl());
+ VarDeclNode &Node = varNodeFor(VD);
auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
if (!Chain) {
Chain = Generator.acquireMemberSpecChain();
@@ -1136,7 +1175,7 @@ class IncrementalStateTracker {
return *Chain;
}
MemberSpecializationChain &memberSpecChainFor(const CXXRecordDecl *RD) {
- CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
+ CXXClassDeclNode &Node = recordNodeFor(RD);
auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
if (!Chain) {
Chain = Generator.acquireMemberSpecChain();
@@ -1145,86 +1184,103 @@ class IncrementalStateTracker {
return *Chain;
}
MemberSpecializationChain &memberSpecChainFor(const EnumDecl *ED) {
- EnumDeclNode &Node = enumNodeFor(ED->getCanonicalDecl());
+ EnumDeclNode &Node = enumNodeFor(ED);
if (!Node.MemberSpec)
Node.MemberSpec = Generator.acquireMemberSpecChain();
return *Node.MemberSpec;
}
- RecordDeclDefinitionDataChain *getChainFor(const CXXRecordDecl *RD) const {
- CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
- return Node.DefData;
+ const RecordDeclDefinitionDataChain *
+ getChainFor(const CXXRecordDecl *RD) const {
+ CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(RD);
+ return N ? N->DefData : nullptr;
}
-
- SpecializationChain *
- getChainFor(const ClassTemplateSpecializationDecl *Spec) const {
- CXXClassDeclNode &Node = recordNodeFor(
- cast<ClassTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
- return Node.Spec.dyn_cast<SpecializationChain *>();
+ const SpecializationChain *
+ getChainFor(const ClassTemplateSpecializationDecl *S) const {
+ CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(S);
+ return N ? N->Spec.dyn_cast<SpecializationChain *>() : nullptr;
}
- VarSpecializationChain *
+ const VarSpecializationChain *
getChainFor(const VarTemplateSpecializationDecl *Spec) const {
- VarDeclNode &Node = varNodeFor(
- cast<VarTemplateSpecializationDecl>(Spec->getCanonicalDecl()));
- return Node.Spec.dyn_cast<VarSpecializationChain *>();
+ VarDeclNode *N = lookupNode<VarDeclNode>(Spec);
+ return N ? N->Spec.dyn_cast<VarSpecializationChain *>() : nullptr;
}
- FunctionSpecializationChain *getChainFor(const FunctionDecl *FD) const {
- FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
- return Node.Spec.dyn_cast<FunctionSpecializationChain *>();
+ const FunctionSpecializationChain *
+ getChainFor(const FunctionDecl *Spec) const {
+ FunctionDeclNode *N = lookupNode<FunctionDeclNode>(Spec);
+ return N ? N->Spec.dyn_cast<FunctionSpecializationChain *>() : nullptr;
}
- MemberSpecializationChain *
+ const MemberSpecializationChain *
getMemberSpecChainFor(const FunctionDecl *FD) const {
- FunctionDeclNode &Node = functionNodeFor(FD->getCanonicalDecl());
- return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+ FunctionDeclNode *N = lookupNode<FunctionDeclNode>(FD);
+ return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
}
- MemberSpecializationChain *getMemberSpecChainFor(const VarDecl *VD) const {
- VarDeclNode &Node = varNodeFor(VD->getCanonicalDecl());
- return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+
+ const MemberSpecializationChain *
+ getMemberSpecChainFor(const VarDecl *VD) const {
+ VarDeclNode *N = lookupNode<VarDeclNode>(VD);
+ return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
}
- MemberSpecializationChain *
+
+ const MemberSpecializationChain *
getMemberSpecChainFor(const CXXRecordDecl *RD) const {
- CXXClassDeclNode &Node = recordNodeFor(RD->getCanonicalDecl());
- return Node.Spec.dyn_cast<MemberSpecializationChain *>();
+ CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(RD);
+ return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
}
- MemberSpecializationChain *getMemberSpecChainFor(const EnumDecl *ED) const {
- EnumDeclNode &Node = enumNodeFor(ED->getCanonicalDecl());
- return Node.MemberSpec;
+
+ const MemberSpecializationChain *
+ getMemberSpecChainFor(const EnumDecl *ED) const {
+ EnumDeclNode *N = lookupNode<EnumDeclNode>(ED);
+ return N ? N->MemberSpec : nullptr;
}
public:
IncrementalStateTracker(ASTContext &Ctx, LangMode M) : Ctx(Ctx), Mode(M) {}
- void beginPTU(llvm::SlabCheckPoint CP) {
- CurID = NextID;
- PTUSlabCheckpoints.recordCheckpoint(CurID, CP);
- PTUStateInfos.try_emplace(CurID, PTUStateInfo{CurID});
- ++NextID;
+ LangMode getMode() const { return Mode; }
+
+ PTUStateInfo ¤t() const {
+ assert(!PTUStack.empty() && "no PTU has been started");
+ return PTUStack.back();
}
- PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
+ PTUID currentID() const { return current().ID; }
+ bool hasOpenPTU() const { return !PTUStack.empty(); }
- bool isFromThisPTU(const void *Ptr, PTUID ID) {
- return PTUSlabCheckpoints.isFromThisPTU(Ctx, Ptr, ID);
+ void beginPTU(llvm::SlabCheckPoint CP) {
+ PTUID ID = NextID++;
+ PTUSlabCheckpoints.recordCheckpoint(ID, CP);
+ PTUStack.emplace_back(ID);
}
- PTUStateInfo ¤t() const {
- assert(NextID > 0 && "no PTU has been started");
- auto It = PTUStateInfos.find(CurID);
- assert(It != PTUStateInfos.end());
- return It->second;
+ bool isFromThisPTU(const void *Ptr, PTUID ID) const {
+ return PTUSlabCheckpoints.isFromThisPTU(Ctx, Ptr, ID);
+ }
+ bool isFromCurrentPTU(const void *Ptr) const {
+ return isFromThisPTU(Ptr, currentID());
}
+ SweepTracker &getHiddenMutationTracker() { return HiddenMutationTracker; }
+ PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
+
void undoLastEntries();
private:
+ void popCurrentPTU() {
+ PTUID ID = currentID();
+ PTUStack.pop_back();
+ PTUSlabCheckpoints.undoFrom(ID);
+ NextID = ID;
+ }
+
template <typename ChainT>
- static bool rollbackChainField(ChainT *&Field, PTUID ID,
- LinkedDeclNodeGenerator &Gen);
+ static bool removeChainField(ChainT *&Field, PTUID ID,
+ LinkedDeclNodeGenerator &Gen);
- static bool rollbackRecordSpec(
+ static bool removeRecordSpec(
llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *>
&Spec,
PTUID ID, LinkedDeclNodeGenerator &Gen);
@@ -1285,34 +1341,12 @@ class PTUMutationActions {
void commitEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec,
bool IsNew);
- // static DeclShape classifyShape(const Decl *D);
-
- // class DeclStateCommitProxy;
-
- // class DeclStateRestoreProxy;
-
template <typename DeclStateProxyT>
void walkDecls(const DeclContext *DC, DeclStateProxyT &Proxy);
// static uint32_t classifyPossibleKinds(DeclShape S, const Decl *D);
- /// Given a Decl already known to be DeclShape S with FlaggedKinds set
- /// (more than one bit at once is the normal case, not an edge case --
- /// a single FunctionDecl can be MSI-backed AND have an unresolved
- /// exception spec at the same time), returns the subset of
- /// FlaggedKinds that actually differ from the last recorded snapshot.
- /// Real chain access, not a guess: builds a fresh footprint the same
- /// way commit() does and compares it against chainFor/
- /// memberSpecChainFor's mostRecent() via the same operator== every
- /// StateAwareChain already uses for its own dedup. A bit with no
- /// chain anywhere in this file (DeducedReturnType has none -- see its
- /// own listener override's comment; TypeForDeclChanged/
- /// EvaluatedValueCached likewise) is never returned as verified --
- /// there is nothing to compare it against, so it cannot be confirmed,
- /// full stop, not assumed either way.
- // static uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t
- // FlaggedKinds,
- // PTUID ID);
+ uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
void commitLevel1(PTUID ID, const Decl *D, MutationRecord &Rec,
bool IsNew = false);
>From 197a9d7fcc86b302f153ca618c55971890b09407 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Thu, 24 Sep 2026 16:08:07 +0530
Subject: [PATCH 07/13] Add initial ErrRecovery implementation and refactor
code
---
.../include/clang/Interpreter/ErrorRecovery.h | 511 +++----
clang/lib/Interpreter/CMakeLists.txt | 1 +
clang/lib/Interpreter/ErrRecovery.cpp | 1321 +++++++++++++++++
3 files changed, 1520 insertions(+), 313 deletions(-)
create mode 100644 clang/lib/Interpreter/ErrRecovery.cpp
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 7421f28110955..61380ec6ea0ad 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -20,6 +20,7 @@
namespace clang {
class ASTContext;
+class Sema;
/// Index of a per PTU State.
using PTUID = unsigned;
@@ -35,102 +36,19 @@ using PTUID = unsigned;
// This footprint exists purely to undo the effects of implicit special
// member generation.
struct DefinitionDataFootprint {
- unsigned Aggregate : 1;
- unsigned PlainOldData : 1;
- unsigned Empty : 1;
- unsigned Polymorphic : 1;
- unsigned IsStandardLayout : 1;
- unsigned IsCXX11StandardLayout : 1;
- unsigned HasTrivialSpecialMembers : 6;
- unsigned HasTrivialSpecialMembersForCall : 6;
- unsigned DeclaredNonTrivialSpecialMembers : 6;
- unsigned DeclaredNonTrivialSpecialMembersForCall : 6;
- unsigned HasIrrelevantDestructor : 1;
- unsigned HasConstexprNonCopyMoveConstructor : 1;
- unsigned HasDefaultedDefaultConstructor : 1;
- unsigned HasConstexprDefaultConstructor : 1;
- unsigned HasDeclaredCopyConstructorWithConstParam : 1;
- unsigned HasDeclaredCopyAssignmentWithConstParam : 1;
- unsigned IsAnyDestructorNoReturn : 1;
- unsigned DeclaredSpecialMembers : 6;
+#define FIELD(Name, Width, Merge) unsigned Name : Width;
+#include "clang/AST/CXXRecordDeclDefinitionBits.def"
bool operator==(const DefinitionDataFootprint &O) const {
- return Aggregate == O.Aggregate && PlainOldData == O.PlainOldData &&
- Empty == O.Empty && Polymorphic == O.Polymorphic &&
- IsStandardLayout == O.IsStandardLayout &&
- IsCXX11StandardLayout == O.IsCXX11StandardLayout &&
- HasTrivialSpecialMembers == O.HasTrivialSpecialMembers &&
- HasTrivialSpecialMembersForCall ==
- O.HasTrivialSpecialMembersForCall &&
- DeclaredNonTrivialSpecialMembers ==
- O.DeclaredNonTrivialSpecialMembers &&
- DeclaredNonTrivialSpecialMembersForCall ==
- O.DeclaredNonTrivialSpecialMembersForCall &&
- HasIrrelevantDestructor == O.HasIrrelevantDestructor &&
- HasConstexprNonCopyMoveConstructor ==
- O.HasConstexprNonCopyMoveConstructor &&
- HasDefaultedDefaultConstructor == O.HasDefaultedDefaultConstructor &&
- HasConstexprDefaultConstructor == O.HasConstexprDefaultConstructor &&
- HasDeclaredCopyConstructorWithConstParam ==
- O.HasDeclaredCopyConstructorWithConstParam &&
- HasDeclaredCopyAssignmentWithConstParam ==
- O.HasDeclaredCopyAssignmentWithConstParam &&
- IsAnyDestructorNoReturn == O.IsAnyDestructorNoReturn &&
- DeclaredSpecialMembers == O.DeclaredSpecialMembers;
+#define FIELD(Name, Width, Merge) \
+ if (Name != O.Name) \
+ return false;
+#include "clang/AST/CXXRecordDeclDefinitionBits.def"
+ return true;
}
bool operator!=(const DefinitionDataFootprint &O) const {
return !(*this == O);
}
-
- // void update(const CXXRecordDecl::DefinitionData &Live) {
- // Aggregate = Live.Aggregate;
- // PlainOldData = Live.PlainOldData;
- // Empty = Live.Empty;
- // Polymorphic = Live.Polymorphic;
- // IsStandardLayout = Live.IsStandardLayout;
- // IsCXX11StandardLayout = Live.IsCXX11StandardLayout;
- // HasTrivialSpecialMembers = Live.HasTrivialSpecialMembers;
- // HasTrivialSpecialMembersForCall = Live.HasTrivialSpecialMembersForCall;
- // DeclaredNonTrivialSpecialMembers = Live.DeclaredNonTrivialSpecialMembers;
- // DeclaredNonTrivialSpecialMembersForCall =
- // Live.DeclaredNonTrivialSpecialMembersForCall;
- // HasIrrelevantDestructor = Live.HasIrrelevantDestructor;
- // HasConstexprNonCopyMoveConstructor =
- // Live.HasConstexprNonCopyMoveConstructor;
- // HasDefaultedDefaultConstructor = Live.HasDefaultedDefaultConstructor;
- // HasConstexprDefaultConstructor = Live.HasConstexprDefaultConstructor;
- // HasDeclaredCopyConstructorWithConstParam =
- // Live.HasDeclaredCopyConstructorWithConstParam;
- // HasDeclaredCopyAssignmentWithConstParam =
- // Live.HasDeclaredCopyAssignmentWithConstParam;
- // IsAnyDestructorNoReturn = Live.IsAnyDestructorNoReturn;
- // DeclaredSpecialMembers = Live.DeclaredSpecialMembers;
- // }
-
- // void restore(CXXRecordDecl::DefinitionData &Live) const {
- // Live.Aggregate = Aggregate;
- // Live.PlainOldData = PlainOldData;
- // Live.Empty = Empty;
- // Live.Polymorphic = Polymorphic;
- // Live.IsStandardLayout = IsStandardLayout;
- // Live.IsCXX11StandardLayout = IsCXX11StandardLayout;
- // Live.HasTrivialSpecialMembers = HasTrivialSpecialMembers;
- // Live.HasTrivialSpecialMembersForCall = HasTrivialSpecialMembersForCall;
- // Live.DeclaredNonTrivialSpecialMembers = DeclaredNonTrivialSpecialMembers;
- // Live.DeclaredNonTrivialSpecialMembersForCall =
- // DeclaredNonTrivialSpecialMembersForCall;
- // Live.HasIrrelevantDestructor = HasIrrelevantDestructor;
- // Live.HasConstexprNonCopyMoveConstructor =
- // HasConstexprNonCopyMoveConstructor;
- // Live.HasDefaultedDefaultConstructor = HasDefaultedDefaultConstructor;
- // Live.HasConstexprDefaultConstructor = HasConstexprDefaultConstructor;
- // Live.HasDeclaredCopyConstructorWithConstParam =
- // HasDeclaredCopyConstructorWithConstParam;
- // Live.HasDeclaredCopyAssignmentWithConstParam =
- // HasDeclaredCopyAssignmentWithConstParam;
- // Live.IsAnyDestructorNoReturn = IsAnyDestructorNoReturn;
- // Live.DeclaredSpecialMembers = DeclaredSpecialMembers;
- // }
};
// Stores the state of a class template specialization. Tracks its
@@ -234,17 +152,15 @@ struct FunctionSpecializationFootprint {
};
// Stores the state of an ordinary class member created from a class template
-// instantiation. Tracks its specialization kind, point of instantiation, and
-// source location so the state can be compared and restored.
+// instantiation. Tracks its specialization kind and point of instantiation
+// so the state can be compared and restored.
struct MemberSpecializationFootprint {
unsigned SpecializationKind : 3;
SourceLocation PointOfInstantiation;
- SourceLocation Location;
bool operator==(const MemberSpecializationFootprint &O) const {
return SpecializationKind == O.SpecializationKind &&
- PointOfInstantiation == O.PointOfInstantiation &&
- Location == O.Location;
+ PointOfInstantiation == O.PointOfInstantiation;
}
bool operator!=(const MemberSpecializationFootprint &O) const {
return !(*this == O);
@@ -254,7 +170,6 @@ struct MemberSpecializationFootprint {
const MemberSpecializationInfo *MSI = Live.getMemberSpecializationInfo();
SpecializationKind = MSI->getTemplateSpecializationKind();
PointOfInstantiation = MSI->getPointOfInstantiation();
- Location = Live.getLocation();
}
template <typename OwnerT> void restore(OwnerT &Live) const {
@@ -262,10 +177,12 @@ struct MemberSpecializationFootprint {
MSI->setTemplateSpecializationKind(
static_cast<TemplateSpecializationKind>(SpecializationKind));
MSI->setPointOfInstantiation(PointOfInstantiation);
- Live.setLocation(Location);
}
};
+class PTUMutationActions;
+class PTUCheckpointLedger;
+
class DeclStateReverter {
private:
struct TemplateCommonAccess : RedeclarableTemplateDecl {
@@ -286,6 +203,16 @@ class DeclStateReverter {
using ClassTemplateDecl::getCommonPtr;
};
+ struct ClassTemplateSpecAccess : ClassTemplateDecl {
+ using ClassTemplateDecl::getSpecializations;
+ };
+ struct FunctionTemplateSpecAccess : FunctionTemplateDecl {
+ using FunctionTemplateDecl::getSpecializations;
+ };
+ struct VarTemplateSpecAccess : VarTemplateDecl {
+ using VarTemplateDecl::getSpecializations;
+ };
+
static bool canonInjectedTSTValid(const ClassTemplateDecl &CTD) {
auto *Ptr =
static_cast<const ClassTemplateCommonAccess &>(CTD).getCommonPtr();
@@ -436,14 +363,13 @@ class DeclStateReverter {
// True if Common could still be created later -- i.e. nobody has called
// getCommonPtr() anywhere in this template's redecl chain yet.
- static bool needToTrackCommonPtr(const RedeclarableTemplateDecl &RT) {
+ static bool isCommonPtrValid(const RedeclarableTemplateDecl &RT) {
return !rawCommonPtr(RT);
}
// True if CanonInjectedTST could still be cached later. Only valid to
// call once Common itself is confirmed to exist (see needToTrackCommonPtr).
- static bool
- needToTrackTemplateCanonInjectedTST(const ClassTemplateDecl *CTD) {
+ static bool isTemplateCanonInjectedTSTValid(const ClassTemplateDecl *CTD) {
return !canonInjectedTSTValid(*CTD);
}
@@ -458,6 +384,19 @@ class DeclStateReverter {
clearCanonInjectedTST(CTD);
}
+ // Specializations are appended in commit order, and rollback is strictly
+ // LIFO (only the most recently committed PTU is ever rolled back). So all
+ // specializations added by a given PTU form a trailing run at the back of
+ // the FoldingSetVector. Popping entries while the back entry belongs to
+ // this PTU is therefore sufficient.
+ //
+ // FoldingSetVector also has no arbitrary-position erase – only
+ // pop_back()/clear() – which is another reason to walk from the back
+ // rather than filter the entries in place.
+ static void removeSpecializations(PTUCheckpointLedger &Ledger,
+ const RedeclarableTemplateDecl *TD,
+ PTUID ID);
+
// static const Type *getRawTypeForDecl(const TypeDecl *TD) {
// return TD->TypeForDecl;
// }
@@ -470,40 +409,36 @@ class DeclStateReverter {
// static void resetTypeForDecl(TypeDecl *TD) { TD->TypeForDecl = nullptr; }
private:
- ASTContext &Ctx;
- llvm::SlabCheckPoint SlabCP;
- llvm::SmallPtrSet<const DeclContext *, 8> RepairedLexicalContexts;
-
- bool isAfterCP(const void *P) const {
- return Ctx.getAllocator().isAfterCheckpoint(P, SlabCP);
- }
+ PTUCheckpointLedger &PTUSlabCheckpoints;
public:
- DeclStateReverter(ASTContext &Ctx, llvm::SlabCheckPoint CP)
- : Ctx(Ctx), SlabCP(CP) {}
+ friend PTUMutationActions;
+
+ explicit DeclStateReverter(PTUCheckpointLedger &Ledger)
+ : PTUSlabCheckpoints(Ledger) {}
static bool isExtensibleContainer(const Decl *D) {
return isa<NamespaceDecl>(D) || isa<CXXRecordDecl>(D);
}
- static bool isRedeclarableOrOnlyDecl(Decl *D) {
- return D->getPreviousDecl() != nullptr;
+ static bool hasNoPreviousDecl(Decl *D) {
+ return D->getPreviousDecl() == nullptr;
}
void detachDefData(const Decl *D);
- void detachCommonBase(const Decl *D);
+ static void detachCommonBase(const RedeclarableTemplateDecl *RT);
/// Remove D from its semantic context's lookup map, reinstating the
/// previous declaration if D had replaced one in-place (which is what
/// StoredDeclsList::HandleRedeclaration does on a redeclaration --
/// erasing the slot outright would lose the older decl entirely; that
/// is the ReopenNs failure).
- void detachFromDCLookup(const Decl *D);
+ void detachFromDCLookup(Decl *D, PTUID ID);
// Walk D's redecl chain looking for the newest decl that predates this
// PTU. Returns nullptr if the entire chain was created this PTU.
- template <typename DeclT> DeclT *findSurvivor(DeclT *D) const;
+ template <typename DeclT> DeclT *findSurvivor(DeclT *D, PTUID ID) const;
template <typename decl_type>
void patchRedeclLink(Redeclarable<decl_type> *D, decl_type *Survivor) {
@@ -512,12 +447,12 @@ class DeclStateReverter {
/// Point the canonical decl's "most recent" link back at the newest
/// redeclaration that predates this PTU.
- void detachFromRedeclChain(const Decl *D);
+ void detachFromRedeclChain(const Decl *D, PTUID ID);
- void repairLexicalChain(DeclContext &DC);
+ void repairLexicalChain(DeclContext &DC, PTUID ID);
private:
- NamedDecl *tryDetachRedeclChain(Decl *D);
+ NamedDecl *tryDetachRedeclChain(Decl *D, PTUID ID);
};
template <typename DataT> struct Snapshot {
@@ -552,12 +487,12 @@ template <typename DataT> class StateAwareChain {
History.push_back(Snapshot<DataT>{ID, Fresh});
}
- const DataT *getPrevious(PTUID ID) const {
- for (auto It = History.rbegin(); It != History.rend(); ++It)
- if (It->ID < ID)
- return It->Data;
- return nullptr;
- }
+ // const DataT *getPrevious(PTUID ID) const {
+ // for (auto It = History.rbegin(); It != History.rend(); ++It)
+ // if (It->ID < ID)
+ // return It->Data;
+ // return nullptr;
+ // }
/// removal: drop every entry with ID >= \p ID.
void removeFrom(PTUID ID) {
@@ -574,10 +509,24 @@ using FunctionSpecializationChain =
using MemberSpecializationChain =
StateAwareChain<MemberSpecializationFootprint>;
+/// Maps addresses to the PTU that allocated them, by keeping the slab
+/// checkpoint taken before each PTU began.
+///
+/// PTU IDs are the vector index, which is what makes the lookup a binary
+/// search: checkpoints are monotonically increasing, so "is this address
+/// after checkpoint N" is true for all N up to the allocating PTU and
+/// false after.
class PTUCheckpointLedger {
+ const ASTContext &Ctx;
llvm::SmallVector<llvm::SlabCheckPoint, 16> CheckpointBeforePTU;
+ bool isAfter(const void *Ptr, PTUID ID) const {
+ return Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
+ }
+
public:
+ explicit PTUCheckpointLedger(const ASTContext &Ctx) : Ctx(Ctx) {}
+
/// Called once, right before parsing PTU \p ID begins.
void recordCheckpoint(PTUID ID, llvm::SlabCheckPoint CP) {
assert(ID == CheckpointBeforePTU.size() &&
@@ -585,49 +534,30 @@ class PTUCheckpointLedger {
CheckpointBeforePTU.push_back(CP);
}
- /// \return the PTU that allocated \p Ptr, or std::nullopt if \p Ptr
- /// predates the oldest recorded checkpoint.
- ///
- /// Walks newest-to-oldest: checkpoints only ever move forward for state
- /// that has survived (committed PTUs are never rewound), so the first
- /// checkpoint for which \p Ptr is "after" is the PTU that produced it.
- std::optional<PTUID> attribute(const ASTContext &Ctx, const void *Ptr) const {
- for (PTUID ID = CheckpointBeforePTU.size(); ID-- > 0;) {
- if (Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]))
- return ID;
- }
- return std::nullopt;
+ /// Was \p Ptr allocated during PTU \p ID or later?
+ bool isFromThisPTU(const void *Ptr, PTUID ID) const {
+ assert(ID < CheckpointBeforePTU.size());
+ return isAfter(Ptr, ID);
}
- inline std::optional<PTUID> attributeByAddress(const ASTContext &Ctx,
- const void *Ptr) {
- // Binary search for the largest ID whose checkpoint Ptr is after -- i.e.
- // the newest PTU boundary this address was allocated on or past.
+ /// The PTU that allocated \p Ptr, or nullopt if it predates the oldest
+ /// recorded checkpoint. The newest ID whose checkpoint \p Ptr is after.
+ std::optional<PTUID> attribute(const void *Ptr) const {
size_t Lo = 0, Hi = CheckpointBeforePTU.size();
std::optional<PTUID> Result;
while (Lo < Hi) {
size_t Mid = Lo + (Hi - Lo) / 2;
- if (Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[Mid])) {
+ if (isAfter(Ptr, Mid)) {
Result = static_cast<PTUID>(Mid);
- Lo = Mid + 1; // still after Mid's checkpoint -- look for a later one
+ Lo = Mid + 1;
} else {
- Hi = Mid; // not even after Mid -- must be before it
+ Hi = Mid;
}
}
return Result;
}
- bool predatesPTU(const ASTContext &Ctx, const void *Ptr, PTUID ID) const {
- assert(ID < CheckpointBeforePTU.size());
- return !Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
- }
-
- bool isFromThisPTU(const ASTContext &Ctx, const void *Ptr, PTUID ID) const {
- assert(ID < CheckpointBeforePTU.size());
- return Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
- }
-
- /// Drop checkpoints from \p ID onward.
+ /// Drop checkpoints from \p ID onward, after rolling those PTUs back.
void undoFrom(PTUID ID) {
if (ID < CheckpointBeforePTU.size())
CheckpointBeforePTU.resize(ID);
@@ -651,11 +581,11 @@ template <typename OwnerT, typename ValueT> class FieldMutationChain {
Entries.push_back(FieldMutation<ValueT>{ID, OldValue});
}
- const ValueT *mostRecent(const OwnerT *Owner) const {
+ std::optional<ValueT> mostRecent(const OwnerT *Owner) const {
auto It = Log.find(Owner);
- return (It == Log.end() || It->second.empty())
- ? nullptr
- : &It->second.back().OldValue;
+ if (It == Log.end() || It->second.empty())
+ return std::nullopt;
+ return It->second.back().OldValue;
}
std::optional<PTUID> mostRecentID(const OwnerT *Owner) const {
@@ -676,24 +606,24 @@ template <typename OwnerT, typename ValueT> class FieldMutationChain {
: std::optional<PTUID>(History.front().ID);
}
- template <typename FnT> void forEachOwnerSince(PTUID ID, FnT &&Fn) const {
- for (auto &Entry : Log)
- if (!Entry.second.empty() && Entry.second.back().ID >= ID)
- Fn(Entry.first);
- }
+ // template <typename FnT> void forEachOwnerSince(PTUID ID, FnT &&Fn) const {
+ // for (auto &Entry : Log)
+ // if (!Entry.second.empty() && Entry.second.back().ID >= ID)
+ // Fn(Entry.first);
+ // }
- void removeFrom(PTUID ID) {
- llvm::SmallVector<OwnerT *> ToErase;
- for (auto &Entry : Log) {
- auto &Entries = Entry.second;
- while (!Entries.empty() && Entries.back().ID >= ID)
- Entries.pop_back();
- if (Entries.empty())
- ToErase.push_back(Entry.first);
- }
- for (const OwnerT *O : ToErase)
- Log.erase(O);
- }
+ // void removeFrom(PTUID ID) {
+ // llvm::SmallVector<OwnerT *> ToErase;
+ // for (auto &Entry : Log) {
+ // auto &Entries = Entry.second;
+ // while (!Entries.empty() && Entries.back().ID >= ID)
+ // Entries.pop_back();
+ // if (Entries.empty())
+ // ToErase.push_back(Entry.first);
+ // }
+ // for (const OwnerT *O : ToErase)
+ // Log.erase(O);
+ // }
/// Same trim-then-erase-if-empty as removeFrom(ID), scoped to one owner
/// instead of sweeping the whole Log -- for callers that already know
@@ -760,11 +690,9 @@ struct MutationRecord {
using DeclShape = MutationRecord::DeclShape;
using MutationType = MutationRecord::MutationKind;
-class PTUMutationActions;
-
struct PTUStateInfo {
PTUID ID;
- const TranslationUnitDecl *ThisTU; // current info
+ const TranslationUnitDecl *ThisPTU; // current info
llvm::MapVector<const Decl *, MutationRecord> Mutations;
@@ -775,17 +703,9 @@ struct PTUStateInfo {
/// Exactly the decls this PTU's own commit() pushed a footprint-chain
/// entry for (DefinitionData/Specialization/MemberSpecialization, via
- /// chainFor()/memberSpecChainFor()), keyed on the canonical decl the
- /// same way those accessors are. Lets undo walk only what this PTU
- /// actually touched instead of sweeping every decl ever tracked across
- /// every PTU -- the exact opposite of commit(), scoped the same way
- /// commit() itself is scoped.
+ /// chainFor()/memberSpecChainFor()).
llvm::SmallPtrSet<const Decl *, 8> TouchedFootprintDecls;
- /// Same idea for the two FieldMutationChain logs (FunctionTypeMutations,
- /// TagdeclInfos), which are keyed by owner rather than living in
- /// LinkedDecls -- kept as two separate sets since the owner types
- /// (FunctionDecl vs TagDecl) differ.
llvm::SmallPtrSet<const FunctionDecl *, 4> TouchedFunctionTypeOwners;
llvm::SmallPtrSet<const TagDecl *, 4> TouchedTagdeclOwners;
@@ -852,10 +772,11 @@ class SweepTracker {
Active.erase(It);
}
- // Call at commit(ID) time. PTUMutationActions is invoked as
+ // Call at commit/restore time. OnConfirmed is invoked as
// (const Decl *D, DeclShape S, uint32_t ConfirmedKinds) for every decl
// that had something newly confirmed this sweep.
- void sweep(PTUMutationActions &Act);
+ template <typename OnConfirmedFn>
+ void sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed);
};
//===----------------------------------------------------------------------===//
@@ -878,105 +799,58 @@ struct CXXClassDeclNode {
PTUID OriginID;
RecordDeclDefinitionDataChain *DefData = nullptr;
llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *> Spec;
- CXXClassDeclNode *Next = nullptr; // free-list link, meaningless off the list
};
struct VarDeclNode {
PTUID OriginID;
llvm::PointerUnion<VarSpecializationChain *, MemberSpecializationChain *>
Spec;
- VarDeclNode *Next = nullptr;
};
struct FunctionDeclNode {
PTUID OriginID;
llvm::PointerUnion<FunctionSpecializationChain *, MemberSpecializationChain *>
Spec;
- FunctionDeclNode *Next = nullptr;
};
struct EnumDeclNode {
PTUID OriginID;
MemberSpecializationChain *MemberSpec =
nullptr; // the only thing an enum can ever have
- EnumDeclNode *Next = nullptr;
};
using DeclLinkedState = llvm::PointerUnion<CXXClassDeclNode *, VarDeclNode *,
FunctionDeclNode *, EnumDeclNode *>;
-/// Intrusive free-list pool for the four node types above. Released nodes
-/// are kept for reuse instead of being immediately deleted. The pool is
-/// bounded by Capacity; once it is full, additional released nodes are
-/// deleted instead of being kept. This keeps memory usage bounded while
-/// still allowing freed nodes to be reused.
-
-template <typename T> class NodePool {
- T *FreeList = nullptr;
- unsigned FreeCount = 0;
- unsigned Capacity;
-
-public:
- explicit NodePool(unsigned Capacity = 64) : Capacity(Capacity) {}
-
- ~NodePool() {
- while (FreeList) {
- T *Dead = FreeList;
- FreeList = FreeList->Next;
- delete Dead;
- }
- }
- /// Hands back a reset (all-default) node -- recycled if one is free,
- /// freshly allocated otherwise.
- T *acquire() {
- if (T *N = FreeList) {
- FreeList = N->Next;
- --FreeCount;
- *N = T();
- return N;
- }
- return new T();
- }
- /// Takes ownership back. Kept for a future acquire() to hand out again
- /// if the free list is under capacity; reclaimed for real (delete)
- /// otherwise, so this pool never holds more than Capacity dead nodes.
- void release(T *N) {
- if (FreeCount >= Capacity) {
- delete N;
- return;
- }
- N->Next = FreeList;
- FreeList = N;
- ++FreeCount;
- }
-};
-
-/// Pool for reusing chain objects without modifying StateAwareChain.
-/// Released chains are kept for reuse, and the pool is bounded by Capacity
-/// so unused chains do not cause unbounded memory growth.
-template <typename ChainT> class ChainPool {
- llvm::SmallVector<ChainT *, 8> Free;
+/// Pool for reusing node/chain objects without requiring any intrusive
+/// free-list field on T itself -- freed pointers are tracked in a side
+/// vector instead, so this works for the four node types above and the
+/// five StateAwareChain-based chain types alike. Released objects are kept
+/// for reuse, and the pool is bounded by Capacity so unused objects do not
+/// cause unbounded memory growth.
+template <typename T> class ObjectPool {
+ llvm::SmallVector<T *, 8> Free;
unsigned Capacity;
public:
- explicit ChainPool(unsigned Capacity = 64) : Capacity(Capacity) {}
+ explicit ObjectPool(unsigned Capacity = 64) : Capacity(Capacity) {}
- ~ChainPool() {
- for (ChainT *C : Free)
+ ~ObjectPool() {
+ for (T *C : Free)
delete C;
}
- ChainT *acquire() {
+ T *acquire() {
if (!Free.empty()) {
- ChainT *C = Free.pop_back_val();
- *C = ChainT();
+ T *C = Free.pop_back_val();
+ *C = T();
return C;
}
- return new ChainT();
+ return new T();
}
- /// Same capacity rule as NodePool::release() -- reclaimed for real once
- /// Free is at capacity, rather than growing without bound.
- void release(ChainT *C) {
+ /// Reclaimed for real once Free is at capacity, rather than growing
+ /// without bound.
+ void release(T *C) {
if (Free.size() >= Capacity) {
delete C;
return;
@@ -986,16 +860,16 @@ template <typename ChainT> class ChainPool {
};
class LinkedDeclNodeGenerator {
- NodePool<CXXClassDeclNode> RecordNodes;
- NodePool<VarDeclNode> VarNodes;
- NodePool<FunctionDeclNode> FunctionNodes;
- NodePool<EnumDeclNode> EnumNodes;
+ ObjectPool<CXXClassDeclNode> RecordNodes;
+ ObjectPool<VarDeclNode> VarNodes;
+ ObjectPool<FunctionDeclNode> FunctionNodes;
+ ObjectPool<EnumDeclNode> EnumNodes;
- ChainPool<RecordDeclDefinitionDataChain> DefDataChains;
- ChainPool<SpecializationChain> ClassSpecChains;
- ChainPool<VarSpecializationChain> VarSpecChains;
- ChainPool<FunctionSpecializationChain> FunctionSpecChains;
- ChainPool<MemberSpecializationChain> MemberSpecChains;
+ ObjectPool<RecordDeclDefinitionDataChain> DefDataChains;
+ ObjectPool<SpecializationChain> ClassSpecChains;
+ ObjectPool<VarSpecializationChain> VarSpecChains;
+ ObjectPool<FunctionSpecializationChain> FunctionSpecChains;
+ ObjectPool<MemberSpecializationChain> MemberSpecChains;
public:
CXXClassDeclNode *acquireRecordNode() { return RecordNodes.acquire(); }
@@ -1055,9 +929,12 @@ enum class LangMode : uint8_t {
class IncrementalStateTracker {
private:
ASTContext &Ctx;
+ // Only needed so remove stale
+ // Sema::SpecialMemberCache entries for decls this PTU created.
+ Sema &SemaRef;
+ PTUCheckpointLedger PTUSlabCheckpoints;
LangMode Mode;
PTUID NextID = 0;
- PTUCheckpointLedger PTUSlabCheckpoints;
mutable std::vector<PTUStateInfo> PTUStack;
@@ -1072,7 +949,7 @@ class IncrementalStateTracker {
SweepTracker HiddenMutationTracker;
FunctionExceptionSpecChain FunctionTypeMutations;
- TypeForDeclChain TagdeclInfos;
+ TypeForDeclChain TagDeclTypes;
friend class PTUMutationActions;
@@ -1083,10 +960,15 @@ class IncrementalStateTracker {
}
void noteTagDeclTypeMutation(PTUID ID, const TagDecl *Owner,
const Type *OldValue) {
- TagdeclInfos.noteMutation(ID, Owner, OldValue);
+ TagDeclTypes.noteMutation(ID, Owner, OldValue);
current().TouchedTagdeclOwners.insert(Owner);
}
+ TypeForDeclChain &getTagDeclTypeInfo() { return TagDeclTypes; }
+ FunctionExceptionSpecChain &getFunctionTypeMutations() {
+ return FunctionTypeMutations;
+ }
+
/// Get-or-create the CXXClassDeclNode backing Canon, acquiring one from
/// the generator on first use. The only place a CXXClassDeclNode is
/// created for this map.
@@ -1238,7 +1120,8 @@ class IncrementalStateTracker {
}
public:
- IncrementalStateTracker(ASTContext &Ctx, LangMode M) : Ctx(Ctx), Mode(M) {}
+ IncrementalStateTracker(ASTContext &Ctx, Sema &S, LangMode M)
+ : Ctx(Ctx), SemaRef(S), PTUSlabCheckpoints(Ctx), Mode(M) {}
LangMode getMode() const { return Mode; }
@@ -1257,7 +1140,7 @@ class IncrementalStateTracker {
}
bool isFromThisPTU(const void *Ptr, PTUID ID) const {
- return PTUSlabCheckpoints.isFromThisPTU(Ctx, Ptr, ID);
+ return PTUSlabCheckpoints.isFromThisPTU(Ptr, ID);
}
bool isFromCurrentPTU(const void *Ptr) const {
return isFromThisPTU(Ptr, currentID());
@@ -1265,6 +1148,7 @@ class IncrementalStateTracker {
SweepTracker &getHiddenMutationTracker() { return HiddenMutationTracker; }
PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
+ Sema &getSema() const { return SemaRef; }
void undoLastEntries();
@@ -1284,6 +1168,14 @@ class IncrementalStateTracker {
llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *>
&Spec,
PTUID ID, LinkedDeclNodeGenerator &Gen);
+ static bool removeVarSpec(
+ llvm::PointerUnion<VarSpecializationChain *, MemberSpecializationChain *>
+ &Spec,
+ PTUID ID, LinkedDeclNodeGenerator &Gen);
+ static bool
+ removeFunctionSpec(llvm::PointerUnion<FunctionSpecializationChain *,
+ MemberSpecializationChain *> &Spec,
+ PTUID ID, LinkedDeclNodeGenerator &Gen);
void removeLinkedDecl(const Decl *D, PTUID ID);
// just only remove entries from current();
};
@@ -1291,11 +1183,23 @@ class IncrementalStateTracker {
class PTUMutationActions {
private:
IncrementalStateTracker &Tracker;
+ SweepTracker &HiddenMutationTracker;
+ DeclStateReverter Reverter;
public:
explicit PTUMutationActions(IncrementalStateTracker &Tracker)
- : Tracker(Tracker) {}
+ : Tracker(Tracker),
+ HiddenMutationTracker(Tracker.getHiddenMutationTracker()),
+ Reverter(Tracker.getPTUSlabCheckpoints()) {}
+
+ template <typename DeclStateProxyT>
+ void walkDecls(const DeclContext *DC, DeclStateProxyT &Proxy);
+
+ uint32_t kindsNeedingTracking(DeclShape S, const Decl *D);
+ uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
+
+private:
/// Every mutation this PTU recorded for a class-shaped decl, applied in
/// dependency order: definition data first (later steps read the completed
/// definition), then the type cache, then specialization footprints, then
@@ -1341,45 +1245,29 @@ class PTUMutationActions {
void commitEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec,
bool IsNew);
- template <typename DeclStateProxyT>
- void walkDecls(const DeclContext *DC, DeclStateProxyT &Proxy);
+ void restoreClass(PTUID ID, const CXXRecordDecl *RD, MutationRecord &Rec);
- // static uint32_t classifyPossibleKinds(DeclShape S, const Decl *D);
+ void restoreFunction(PTUID ID, const FunctionDecl *FD, MutationRecord &Rec);
- uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
+ void restoreTemplate(PTUID ID, const RedeclarableTemplateDecl *TD,
+ MutationRecord &Rec);
+
+ void restoreVar(PTUID ID, const VarDecl *VD, MutationRecord &Rec);
+
+ void restoreEnum(PTUID ID, const EnumDecl *ED, MutationRecord &Rec);
- void commitLevel1(PTUID ID, const Decl *D, MutationRecord &Rec,
- bool IsNew = false);
+ void restoreTypedef(PTUID ID, const TypedefNameDecl *TD, MutationRecord &Rec);
+public:
+ void restoreDecl(PTUID ID, const Decl *D, MutationRecord &Rec);
+ void commitDecl(PTUID ID, const Decl *D, MutationRecord &Rec,
+ bool IsNew = false);
// respect the LIFO so only current inside map not commited can be commited
// not randon PTUID
// global map info shouldn't be commited before only added here. not note*
// time.
void commit(TranslationUnitDecl *MostRecentTU);
-
- void restoreClassFamily(PTUID ID, const CXXRecordDecl *RD,
- MutationRecord &Rec);
-
- void restoreFunctionFamily(PTUID ID, const FunctionDecl *FD,
- MutationRecord &Rec);
-
- void restoreTemplateFamily(PTUID ID, const RedeclarableTemplateDecl *TD,
- MutationRecord &Rec);
-
- void restoreVarFamily(PTUID ID, const VarDecl *VD, MutationRecord &Rec);
-
- /// Every mutation this PTU recorded for an enum-shaped decl, or -- when CR
- /// is non-null -- the baseline seed for one this PTU created.
- ///
- /// The smallest family: enums have no specialization category (there is no
- /// such thing as an enum template), no deferred bodies, and no members with
- /// independent mutable state -- EnumConstantDecls live and die with the
- /// EnumDecl, so they are not separately tracked.
- void restoreEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec);
-
- void restoreLevel1(PTUID ID, const Decl *D, MutationRecord &Rec);
-
- void rollback(TranslationUnitDecl *MostRecentTU);
+ void restore(TranslationUnitDecl *MostRecentTU);
};
class PTUMutationRecorder : public ASTMutationListener {
@@ -1388,51 +1276,48 @@ class PTUMutationRecorder : public ASTMutationListener {
template <typename TemplateT, typename SpecT>
void noteTemplateDeclMutation(const TemplateT *TD, const SpecT *Spec,
- DeclShape S) {}
+ DeclShape S);
void noteExceptionSpecMutation(const FunctionDecl *FD,
- MutationType K = MutationType::ExceptionSpec) {
- }
+ MutationType K = MutationType::ExceptionSpec);
- void noteDefinitionInstantiated(const Decl *D) {}
+ void noteDefinitionInstantiated(const Decl *D);
public:
PTUMutationRecorder(IncrementalStateTracker &Tracker) : Tracker(Tracker) {}
/// A new TagDecl definition was completed.
- void CompletedTagDefinition(const TagDecl *D) override {}
+ void CompletedTagDefinition(const TagDecl *D) override;
/// A new declaration with name has been added to a DeclContext.
- void AddedVisibleDecl(const DeclContext *DC, const Decl *D) override {}
+ void AddedVisibleDecl(const DeclContext *DC, const Decl *D) override;
/// An implicit member was added after the definition was completed.
- void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) override {
- }
+ void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) override;
/// A template specialization (or partial one) was added to the
/// template declaration.
void AddedCXXTemplateSpecialization(
const ClassTemplateDecl *TD,
- const ClassTemplateSpecializationDecl *D) override {}
+ const ClassTemplateSpecializationDecl *D) override;
/// A template specialization (or partial one) was added to the
/// template declaration.
void AddedCXXTemplateSpecialization(
const VarTemplateDecl *TD,
- const VarTemplateSpecializationDecl *D) override {}
+ const VarTemplateSpecializationDecl *D) override;
/// A template specialization (or partial one) was added to the
/// template declaration.
void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD,
- const FunctionDecl *D) override {}
+ const FunctionDecl *D) override;
/// A function's exception specification has been evaluated or
/// instantiated.
- void ResolvedExceptionSpec(const FunctionDecl *FD) override {}
+ void ResolvedExceptionSpec(const FunctionDecl *FD) override;
/// A function's return type has been deduced.
- void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override {
- }
+ void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override;
/// A virtual destructor's operator delete has been resolved.
void ResolvedOperatorDelete(const CXXDestructorDecl *DD,
@@ -1460,7 +1345,7 @@ class PTUMutationRecorder : public ASTMutationListener {
/// The instantiation of a templated function or variable was
/// requested. In particular, the point of instantiation and template
/// specialization kind of \p D may have changed.
- void InstantiationRequested(const ValueDecl *D) override {}
+ void InstantiationRequested(const ValueDecl *D) override;
/// A templated variable's definition was implicitly instantiated.
void VariableDefinitionInstantiated(const VarDecl *D) override {
diff --git a/clang/lib/Interpreter/CMakeLists.txt b/clang/lib/Interpreter/CMakeLists.txt
index 01d3295d1ac30..e2e0c48feb8a6 100644
--- a/clang/lib/Interpreter/CMakeLists.txt
+++ b/clang/lib/Interpreter/CMakeLists.txt
@@ -22,6 +22,7 @@ endif()
add_clang_library(clangInterpreter
DeviceOffload.cpp
CodeCompletion.cpp
+ ErrRecovery.cpp
IncrementalAction.cpp
IncrementalExecutor.cpp
OrcIncrementalExecutor.cpp
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
new file mode 100644
index 0000000000000..b5070b7f47b7b
--- /dev/null
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -0,0 +1,1321 @@
+//===--------- ErrorRecovery.cpp - Declaration State Recovery -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements declaration state recovery for failed
+// partial translation units (PTUs), restoring declaration state to
+// the state of the previous PTU.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclContextInternals.h"
+#include "clang/Interpreter/ErrorRecovery.h"
+#include "clang/Sema/Sema.h"
+
+namespace clang {
+
+/// -----------------------------------------------------------------------
+//////////////////////// PTUMutationActions::Helpers ///////////////////////
+/// -----------------------------------------------------------------------
+
+DefinitionDataFootprint *
+DeclStateReverter::createDefinitionDataFootprint(const ASTContext &Ctx,
+ const CXXRecordDecl &RD) {
+ auto *FP =
+ new (Ctx, alignof(DefinitionDataFootprint)) DefinitionDataFootprint();
+ const auto &Live = RD.data();
+#define FIELD(Name, Width, Merge) FP->Name = Live.Name;
+#include "clang/AST/CXXRecordDeclDefinitionBits.def"
+ return FP;
+}
+
+bool DeclStateReverter::compareDefinitionDataFootprint(
+ const DefinitionDataFootprint &FP, const CXXRecordDecl &RD) {
+ const auto &Live = RD.data();
+#define FIELD(Name, Width, Merge) \
+ if (FP.Name != Live.Name) \
+ return false;
+#include "clang/AST/CXXRecordDeclDefinitionBits.def"
+ return true;
+}
+
+void DeclStateReverter::restoreDefinitionDataFootprint(
+ const DefinitionDataFootprint &FP, CXXRecordDecl &RD) {
+ auto &Live = RD.data();
+#define FIELD(Name, Width, Merge) Live.Name = FP.Name;
+#include "clang/AST/CXXRecordDeclDefinitionBits.def"
+}
+
+// NEEDS: friend class DeclStateReverter; in CXXRecordDecl (DefinitionData
+// is PRIVATE there -- already granted) and nothing further for the
+// DeclContext/TagDecl parts below, which are routed through the
+// protected accessors above instead.
+void DeclStateReverter::restoreDefinitionAndRevertDC(CXXRecordDecl &RD) {
+ // 1. DefinitionData pointer -- back to null.
+ RD.DefinitionData = nullptr;
+
+ // 2. DeclContext member list -- back to empty. RD's own FirstDecl/
+ // LastDecl were populated by InstantiateClassImpl substituting RD's
+ // members for the first time (SemaTemplateInstantiate.cpp:3658-3729,
+ // and for the static_assert/friend-decl case specifically,
+ // Sema::BuildStaticAssertDeclaration's CurContext->addDecl(), which
+ // never notifies since neither is a NamedDecl) -- an incomplete,
+ // never-defined class's DeclContext has neither, so this is RD's
+ // true prior state, not a guess.
+ clearDeclContextChain(RD);
+
+ // 3. TagDecl completion bits -- back to "never started". Flipped by
+ // startDefinition()/completeDefinition().
+ clearBeingDefined(RD);
+ RD.setCompleteDefinition(false);
+}
+
+void DeclStateReverter::revertDefinitionArrival(Decl &D) {
+ if (auto *FD = dyn_cast<FunctionDecl>(&D))
+ FD->setBody(nullptr);
+ else if (auto *VD = dyn_cast<VarDecl>(&D))
+ VD->setInit(nullptr);
+ else if (auto *Field = dyn_cast<FieldDecl>(&D))
+ Field->setInClassInitializer(nullptr);
+}
+
+void DeclStateReverter::removeSpecializations(
+ PTUCheckpointLedger &Ledger, const RedeclarableTemplateDecl *TD, PTUID ID) {
+ if (const auto *CTD = dyn_cast<ClassTemplateDecl>(TD)) {
+ auto &Specs =
+ static_cast<const ClassTemplateSpecAccess &>(*CTD).getSpecializations();
+ while (!Specs.empty()) {
+ auto It = Specs.end();
+ --It;
+ if (!Ledger.isFromThisPTU(&*It, ID))
+ break;
+ Specs.pop_back();
+ }
+ } else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(TD)) {
+ auto &Specs = static_cast<const FunctionTemplateSpecAccess &>(*FTD)
+ .getSpecializations();
+ while (!Specs.empty()) {
+ auto It = Specs.end();
+ --It;
+ if (!Ledger.isFromThisPTU(It->getFunction(), ID))
+ break;
+ Specs.pop_back();
+ }
+ } else if (const auto *VTD = dyn_cast<VarTemplateDecl>(TD)) {
+ auto &Specs =
+ static_cast<const VarTemplateSpecAccess &>(*VTD).getSpecializations();
+ while (!Specs.empty()) {
+ auto It = Specs.end();
+ --It;
+ if (!Ledger.isFromThisPTU(&*It, ID))
+ break;
+ Specs.pop_back();
+ }
+ }
+}
+
+void DeclStateReverter::detachDefData(const Decl *D) {
+ // TODO: handle Template's own separate redecl chain the same way.
+ const auto *RD = dyn_cast<CXXRecordDecl>(D);
+ if (!RD)
+ return;
+ for (const auto *I : RD->redecls())
+ const_cast<CXXRecordDecl *>(cast<CXXRecordDecl>(I))->DefinitionData =
+ nullptr;
+}
+
+void DeclStateReverter::detachCommonBase(const RedeclarableTemplateDecl *RT) {
+ // Common is shared across the WHOLE redecl chain once path-compression
+ // (RedeclarableTemplateDecl::getCommonPtr()'s backward walk-then-
+ // backfill) has run -- resetting only the one decl this PTU created
+ // would leave some other redecl holding a stale, dangling copy of the
+ // same pointer. Walk every redecl, not just a backward chain from RT.
+ for (const auto *R : RT->redecls())
+ clearCommonPtr(*R);
+}
+
+/// Remove D from every lookup map it became visible in, reinstating the
+/// previous declaration where D had replaced one in-place (which is what
+/// StoredDeclsList::HandleRedeclaration does on a redeclaration --
+/// erasing the slot outright would lose the older decl entirely; that is
+/// the ReopenNs failure).
+void DeclStateReverter::detachFromDCLookup(Decl *D, PTUID ID) {
+ NamedDecl *ND = dyn_cast<NamedDecl>(D);
+ if (!ND)
+ return; // nothing nameable -- never entered a lookup map
+
+ // Chain must still be intact to find the replacement -- that is why
+ // this runs before detachFromRedeclChain.
+ NamedDecl *Survivor = findSurvivor(ND, ID);
+ DeclarationName Name = ND->getDeclName();
+
+ // A decl in a transparent context (an unnamed namespace, an unscoped
+ // enum, a linkage-spec block) is visible in the enclosing context's map
+ // as well, so every level it was inserted into needs the same repair --
+ // not just its own primary context.
+ DeclContext *DC = ND->getDeclContext();
+ do {
+ DeclContext *Primary = DC->getPrimaryContext();
+ if (StoredDeclsMap *Map = Primary->getLookupPtr()) {
+ auto Pos = Map->find(Name);
+ // Not an error: the map is built lazily, so a context that was
+ // never looked up in has no entry for this name at all.
+ if (Pos != Map->end()) {
+ StoredDeclsList &List = Pos->second;
+ List.remove(ND);
+ if (Survivor)
+ List.addOrReplaceDecl(Survivor);
+ if (List.isNull())
+ Map->erase(Pos);
+ }
+ }
+ } while (DC->isTransparentContext() && (DC = DC->getParent()));
+
+ // DeclContext *Lexical = const_cast<DeclContext
+ // *>(ND->getLexicalDeclContext()); if
+ // (RepairedLexicalContexts.insert(Lexical).second)
+ // repairLexicalChain(*Lexical);
+}
+
+// Walk D's redecl chain looking for the newest decl that predates this
+// PTU. Returns nullptr if the entire chain was created this PTU.
+template <typename DeclT>
+DeclT *DeclStateReverter::findSurvivor(DeclT *D, PTUID ID) const {
+ for (Decl *It = D->getMostRecentDecl(); It; It = It->getPreviousDecl()) {
+ if (!PTUSlabCheckpoints.isFromThisPTU(D, ID))
+ return dyn_cast<DeclT>(It);
+ }
+ return nullptr;
+}
+
+/// Point the canonical decl's "most recent" link back at the newest
+/// redeclaration that predates this PTU.
+void DeclStateReverter::detachFromRedeclChain(const Decl *D, PTUID ID) {
+ (void)tryDetachRedeclChain(const_cast<Decl *>(D), ID);
+}
+
+void DeclStateReverter::repairLexicalChain(DeclContext &DC, PTUID ID) {
+ auto &Access = static_cast<DeclContextLinkAccess &>(DC);
+ Decl *Prev = nullptr;
+ for (Decl *Cur = Access.FirstDecl; Cur; Cur = Cur->getNextDeclInContext()) {
+ if (PTUSlabCheckpoints.isFromThisPTU(Cur, ID)) {
+ if (Prev) {
+ Access.LastDecl = Prev;
+ clearNextInContext(*Prev);
+ } else {
+ Access.FirstDecl = Access.LastDecl = nullptr; // nothing survives
+ }
+ return;
+ }
+ Prev = Cur;
+ }
+}
+
+NamedDecl *DeclStateReverter::tryDetachRedeclChain(Decl *D, PTUID ID) {
+ if (hasNoPreviousDecl(D))
+ return nullptr; // nothing to do
+
+ NamedDecl *Survivor = nullptr;
+
+ auto unlinkRedeclChain = [&](auto *RD) {
+ auto *SurvivorDecl = findSurvivor(RD, ID);
+ if (SurvivorDecl)
+ patchRedeclLink(RD, SurvivorDecl);
+
+ Survivor = SurvivorDecl;
+ };
+
+ switch (D->getKind()) {
+ case Decl::Function:
+ unlinkRedeclChain(cast<FunctionDecl>(D));
+ break;
+
+ case Decl::Var:
+ unlinkRedeclChain(cast<VarDecl>(D));
+ break;
+
+ case Decl::Enum:
+ case Decl::Record:
+ case Decl::CXXRecord:
+ unlinkRedeclChain(cast<TagDecl>(D));
+ break;
+
+ case Decl::ClassTemplate: {
+ // NamedDecl *ND = cast<RedeclarableTemplateDecl>(D)->getTemplatedDecl();
+ unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
+ // tryDetachRedeclChain(ND);
+ } break;
+
+ case Decl::FunctionTemplate:
+ unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
+ break;
+
+ case Decl::TypeAliasTemplate:
+ unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
+ break;
+
+ case Decl::VarTemplate:
+ unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
+ break;
+
+ case Decl::Namespace:
+ unlinkRedeclChain(cast<NamespaceDecl>(D));
+ break;
+
+ default:
+ break;
+ }
+
+ return Survivor;
+}
+
+static DeclShape classifyShape(const Decl *D) {
+ // Base shape -- most-derived first.
+ if (isa<RedeclarableTemplateDecl>(D))
+ return DeclShape::Template;
+ else if (isa<EnumDecl>(D))
+ return DeclShape::Enum;
+ else if (isa<FunctionDecl>(D))
+ return DeclShape::Function;
+ else if (isa<VarDecl>(D))
+ return DeclShape::Var;
+ else if (isa<CXXRecordDecl>(D))
+ return DeclShape::Class;
+ else if (isa<TypedefDecl, TypeAliasDecl>(D))
+ return DeclShape::Typedef;
+ else
+ return DeclShape::None;
+}
+
+/// Called once when a decl is first seen. This only sets up the possible
+/// MutationRecord kinds; it does not detect the mutation itself.
+///
+/// Add a kind here only if we need to do something before the mutation:
+/// - save the old value because it will be overwritten before the listener
+/// runs, or
+/// - start hidden tracking because there is no listener for that change.
+///
+/// If the listener itself is enough to detect the change, don't add it here.
+/// noteMutated() will create the record when the change actually happens.
+///
+/// Explicit specializations are terminal, so their SpecInfo/MemberSpecInfo
+/// does not need to be tracked.
+uint32_t PTUMutationActions::kindsNeedingTracking(DeclShape S, const Decl *D) {
+ switch (S) {
+ case DeclShape::Class: {
+ const auto *RD = cast<CXXRecordDecl>(D);
+ uint32_t Kinds = 0;
+ // TypeForDecl is written twice for TagDecls. Keep tracking until both
+ // writes are done.
+ // if (const Type *T = DeclStateReverter::getRawTypeForDecl(RD);
+ // !T || T->isCanonicalUnqualified())
+ // Kinds |= uint32_t(MutationType::TypeForDecl);
+ if (RD->hasDefinition() && RD == RD->getDefinition()) {
+ // Do we really need to create a DefData chain here? Usually, an implicit
+ // decl can be mutated later after the definition. If it is already wired
+ // correctly, there is nothing to mutate or track here.
+ //
+ // Save the current DefinitionData because it may change later.
+ if (RD->needsImplicitDefaultConstructor() ||
+ RD->needsImplicitCopyConstructor() ||
+ RD->needsImplicitCopyAssignment() ||
+ RD->needsImplicitMoveConstructor() ||
+ RD->needsImplicitMoveAssignment() || RD->needsImplicitDestructor())
+ Kinds |= uint32_t(MutationType::DefinitionData);
+ }
+ if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+ if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization)
+ Kinds |= uint32_t(MutationType::SpecInfo);
+ } else if (const auto *MSI = RD->getMemberSpecializationInfo()) {
+ // Same terminal state, same enum, reused for MSI's own Kind/POI.
+ if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+ Kinds |= uint32_t(MutationType::MemberSpecInfo);
+ }
+ return Kinds;
+ }
+ case DeclShape::Function: {
+ const auto *FD = cast<FunctionDecl>(D);
+ uint32_t Kinds = 0;
+ const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
+ // Exception-spec resolution changes the type before the listener runs,
+ // so we need the old type saved beforehand.
+ if (FPT && (FPT->getExceptionSpecType() == EST_Unevaluated ||
+ FPT->getExceptionSpecType() == EST_Uninstantiated ||
+ FPT->getExceptionSpecType() == EST_DependentNoexcept ||
+ FPT->getExceptionSpecType() == EST_Unparsed))
+ Kinds |= uint32_t(MutationType::ExceptionSpec);
+ if (FD->getReturnType()->isUndeducedType())
+ // Deduced return type works the same way: the type is changed before
+ // the listener runs, so save it beforehand.
+ Kinds |= uint32_t(MutationType::DeducedReturnType);
+ if (FD->getTemplateSpecializationInfo() &&
+ FD->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+ // A specialization’s Kind/point-of-instantiation is overwritten in place,
+ // with no separate record of the old value. The footprint chain is the
+ // only place where the previous value is preserved.
+ Kinds |= uint32_t(MutationType::SpecInfo);
+ if (const auto *MSI = FD->getMemberSpecializationInfo()) {
+ if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+ Kinds |= uint32_t(MutationType::MemberSpecInfo);
+ }
+
+ return Kinds;
+ }
+ case DeclShape::Var: {
+ const auto *VD = cast<VarDecl>(D);
+ uint32_t Kinds = 0;
+ if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
+ // Same terminal-state exclusion as the Class/Function cases above.
+ if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization)
+ Kinds |= uint32_t(MutationType::SpecInfo);
+ } else if (const auto *MSI = VD->getMemberSpecializationInfo()) {
+ if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+ Kinds |= uint32_t(MutationType::MemberSpecInfo);
+ }
+ // EvaluatedStmt::WasEvaluated is set the first time this variable is
+ // constant-evaluated, which can happen in any PTU long after the VarDecl
+ // was created. There is no ASTMutationListener event for this
+ // (VarDecl::evaluateValueImpl doesn’t notify anything), so this is a
+ // hidden kind that we have to check live here, just like
+ // Shape::Template’s CommonCreated.
+ //
+ // getEvaluatedStmt() is a pure read – unlike evaluateValue(), it doesn’t
+ // trigger evaluation itself, so it’s safe to check on every classify call.
+ if (const EvaluatedStmt *Eval = VD->getEvaluatedStmt();
+ !Eval || !Eval->WasEvaluated)
+ Kinds |= uint32_t(MutationType::EvaluatedValue);
+
+ return Kinds;
+ }
+ case DeclShape::Enum: {
+ const auto *ED = cast<EnumDecl>(D);
+ uint32_t Kinds = 0;
+ // TypeForDecl: EnumDecl is also a TagDecl, so the same two-write pattern
+ // and “still open” condition as the Class case above apply here – see
+ // its comment for the details.
+ //
+ // if (const Type *T = DeclStateReverter::getRawTypeForDecl(ED);
+ // !T || T->isCanonicalUnqualified())
+ // Kinds |= uint32_t(MutationType::TypeForDecl);
+ //
+ // MSI-backed part: for an ordinary enum, its own completion is the
+ // redeclaration-creates-a-new-object case. EnumDecl is Redeclarable just
+ // like CXXRecordDecl, so the same reasoning as Function/Var applies here.
+ if (const auto *MSI = ED->getMemberSpecializationInfo()) {
+ // Same terminal-state exclusion as the other MSI-backed cases above.
+ if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
+ Kinds |= uint32_t(MutationType::MemberSpecInfo);
+ }
+ return Kinds;
+ }
+ case DeclShape::Template: {
+ uint32_t Kinds = 0;
+ // TODO: track only canon decl; and also add check
+ // HiddenMutationTracker.isTrackedFor(RT->getCanonicalDecl()) is not being
+ // already tracked
+ const RedeclarableTemplateDecl *RT =
+ cast<RedeclarableTemplateDecl>(D->getCanonicalDecl());
+ if (!DeclStateReverter::isCommonPtrValid(*RT) &&
+ !HiddenMutationTracker.isTrackedFor(
+ RT, uint32_t(MutationType::TemplateCommon))) {
+ // No listener reports these Common changes, so use hidden tracking.
+ Kinds |= uint32_t(MutationType::TemplateCommon);
+ } else if (const auto *CTD = dyn_cast<ClassTemplateDecl>(RT)) {
+ // Common exists, so calling getCommonPtr() here is safe (won't
+ // allocate) -- only ClassTemplateDecl has CanonInjectedTST in its
+ // own Common, not the Function/Var template siblings.
+ if (!DeclStateReverter::isTemplateCanonInjectedTSTValid(CTD))
+ Kinds |= uint32_t(MutationType::CanonInjectedTST);
+ }
+ return Kinds;
+ }
+ case DeclShape::Typedef: {
+ // TypedefDecl/TypeAliasDecl only -- ASTContext::getTypedefType() has its
+ // own guard (if (Decl->TypeForDecl) // return), so this is effectively
+ // write-once: once non-null, TypeForDecl
+ // never changes again for this decl.
+ // const auto *TD = cast<TypedefNameDecl>(D);
+ // if (!DeclStateReverter::getRawTypeForDecl(TD))
+ // return uint32_t(MutationType::TypeForDecl);
+ return 0;
+ }
+ case DeclShape::None:
+ return 0;
+ }
+ return 0;
+}
+
+/// Given a Decl known to be DeclShape S with FlaggedKinds set, return the
+/// subset of kinds that are actually confirmed.
+///
+/// Everything reaching this function through verifyMutations() belongs to
+/// a decl from an earlier PTU. Same-PTU mutations are ignored by the
+/// reactive listeners, so these mutations were already found during an
+/// earlier commit().
+///
+/// Some mutation kinds are fully confirmed by their listener, so there is
+/// no need to walk the chain for them:
+/// - DefinitionInstantiate, SpecializationAdded, DefinitionData
+/// - ExceptionSpec, DeducedReturnType
+///
+/// For these, the listener itself is the source of truth for the change.
+/// In particular, don’t use the FunctionTypeMutations chain for
+/// ExceptionSpec/DeducedReturnType here. That chain is populated later by
+/// commitFunctionFamily(), so checking it here would miss the first
+/// resolution.
+///
+/// The remaining kinds don’t have a reliable listener, so they need to be
+/// checked against the stored state:
+/// - SpecInfo, MemberSpecInfo
+/// - Template Common/CanonInjectedTST
+/// - Var EvaluatedValue
+/// - Typedef TypeForDecl
+///
+/// For footprint-based checks, the baseline should already be there since
+/// it is seeded when the decl is first discovered. If it’s missing here,
+/// assert in debug builds, but still confirm the mutation. commitDecl()
+/// will create the missing chain while committing, which fixes up the
+/// baseline for future checks.
+uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
+ uint32_t FlaggedKinds) {
+
+ uint32_t Verified = 0;
+ Verified |= FlaggedKinds & uint32_t(MutationType::DefinitionInstantiate);
+ switch (S) {
+ case DeclShape::Class: {
+ const auto *RD = cast<CXXRecordDecl>(D);
+ // AddedCXXImplicitMember firing is itself the confirmation -- see
+ // this function's own top comment for why no chain lookup belongs
+ // here.
+ Verified |= FlaggedKinds & uint32_t(MutationType::DefinitionData);
+ if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
+ // const Type *Now = DeclStateReverter::getRawTypeForDecl(RD);
+ // if (Tracker.getTagDeclTypeInfo().mostRecent(RD).value_or(nullptr) !=
+ // Now)
+ // Verified |= uint32_t(MutationType::TypeForDecl);
+ }
+ if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
+ if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+ auto *Chain = Tracker.getChainFor(Spec);
+ assert(Chain && "SpecInfo baseline missing at verify time");
+ const SpecializationFootprint *Last =
+ Chain ? Chain->mostRecent() : nullptr;
+ if (!Last ||
+ !DeclStateReverter::compareSpecializationFootprint(*Last, *Spec))
+ Verified |= uint32_t(MutationType::SpecInfo);
+ }
+ } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
+ auto *Chain = Tracker.getMemberSpecChainFor(RD);
+ assert(Chain && "MemberSpecInfo baseline missing at verify time");
+ const MemberSpecializationFootprint *Last =
+ Chain ? Chain->mostRecent() : nullptr;
+ if (!Last ||
+ !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *RD))
+ Verified |= uint32_t(MutationType::MemberSpecInfo);
+ }
+ break;
+ }
+ case DeclShape::Function: {
+ const auto *FD = cast<FunctionDecl>(D);
+ if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
+ auto *Chain = Tracker.getChainFor(FD);
+ assert(Chain && "SpecInfo baseline missing at verify time");
+ const FunctionSpecializationFootprint *Last =
+ Chain ? Chain->mostRecent() : nullptr;
+ if (!Last || !DeclStateReverter::compareFunctionSpecializationFootprint(
+ *Last, *FD))
+ Verified |= uint32_t(MutationType::SpecInfo);
+ } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
+ auto *Chain = Tracker.getMemberSpecChainFor(FD);
+ assert(Chain && "MemberSpecInfo baseline missing at verify time");
+ const MemberSpecializationFootprint *Last =
+ Chain ? Chain->mostRecent() : nullptr;
+ if (!Last ||
+ !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *FD))
+ Verified |= uint32_t(MutationType::MemberSpecInfo);
+ }
+ // ResolvedExceptionSpec/DeducedReturnType firing is itself the
+ // confirmation.
+ Verified |= FlaggedKinds & (uint32_t(MutationType::ExceptionSpec) |
+ uint32_t(MutationType::DeducedReturnType));
+ break;
+ }
+ case DeclShape::Var: {
+ const auto *VD = cast<VarDecl>(D);
+ if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
+ if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
+ auto *Chain = Tracker.getChainFor(Spec);
+ assert(Chain && "SpecInfo baseline missing at verify time");
+ const VarSpecializationFootprint *Last =
+ Chain ? Chain->mostRecent() : nullptr;
+ if (!Last ||
+ !DeclStateReverter::compareVarSpecializationFootprint(*Last, *Spec))
+ Verified |= uint32_t(MutationType::SpecInfo);
+ }
+ } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
+ auto *Chain = Tracker.getMemberSpecChainFor(VD);
+ assert(Chain && "MemberSpecInfo baseline missing at verify time");
+ const MemberSpecializationFootprint *Last =
+ Chain ? Chain->mostRecent() : nullptr;
+ if (!Last ||
+ !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *VD))
+ Verified |= uint32_t(MutationType::MemberSpecInfo);
+ }
+ if (FlaggedKinds & uint32_t(MutationType::EvaluatedValue) &&
+ Tracker.getHiddenMutationTracker().isTrackedFor(
+ VD, uint32_t(MutationType::EvaluatedValue))) {
+ // No chain to compare against -- write-once field, so presence of
+ // WasEvaluated=true right now IS the confirmation, same reasoning
+ // as Shape::Template's CommonCreated/CanonInjectedTSTCached above.
+ const EvaluatedStmt *Eval = VD->getEvaluatedStmt();
+ if (Eval && Eval->WasEvaluated)
+ Verified |= uint32_t(MutationType::EvaluatedValue);
+ }
+ break;
+ }
+ case DeclShape::Enum: {
+ const auto *ED = cast<EnumDecl>(D);
+ // if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
+ // const Type *Now = DeclStateReverter::getRawTypeForDecl(ED);
+ // if (Tracker.getTagDeclTypeInfo().mostRecent(ED).value_or(nullptr) !=
+ // Now)
+ // Verified |= uint32_t(MutationType::TypeForDecl);
+ // }
+ if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
+ auto *Chain = Tracker.getMemberSpecChainFor(ED);
+ assert(Chain && "MemberSpecInfo baseline missing at verify time");
+ const MemberSpecializationFootprint *Last =
+ Chain ? Chain->mostRecent() : nullptr;
+ if (!Last ||
+ !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *ED))
+ Verified |= uint32_t(MutationType::MemberSpecInfo);
+ }
+ break;
+ }
+ case DeclShape::Template: {
+ // SpecializationAdded is known directly from
+ // AddedCXXTemplateSpecialization firing, so
+ // always confirmed when flagged.
+ Verified |= FlaggedKinds & uint32_t(MutationType::SpecializationAdded);
+ const auto *RT = cast<RedeclarableTemplateDecl>(D)->getCanonicalDecl();
+ if (FlaggedKinds & uint32_t(MutationType::TemplateCommon) &&
+ Tracker.getHiddenMutationTracker().isTrackedFor(
+ RT, uint32_t(MutationType::TemplateCommon))) {
+ // No chain to compare against -- write-once, so Common's own
+ // existence right now IS the confirmation.
+ if (DeclStateReverter::isCommonPtrValid(*RT))
+ Verified |= uint32_t(MutationType::TemplateCommon);
+ }
+ if (FlaggedKinds & uint32_t(MutationType::CanonInjectedTST) &&
+ Tracker.getHiddenMutationTracker().isTrackedFor(
+ RT, uint32_t(MutationType::CanonInjectedTST))) {
+ if (const auto *CTD = dyn_cast<ClassTemplateDecl>(RT))
+ if (DeclStateReverter::isTemplateCanonInjectedTSTValid(CTD))
+ Verified |= uint32_t(MutationType::CanonInjectedTST);
+ }
+ } break;
+ case DeclShape::Typedef: {
+ const auto *TD = cast<TypedefNameDecl>(D);
+ if (FlaggedKinds & uint32_t(MutationType::TypeForDecl) &&
+ // Defensive, on top of SweepTracker::sweep()'s own bookkeeping --
+ // same reasoning as the Var/Template cases above.
+ Tracker.getHiddenMutationTracker().isTrackedFor(
+ TD, uint32_t(MutationType::TypeForDecl))) {
+ // No chain to compare against -- write-once field (unlike
+ // TagDecl's TypeForDecl, this one is guarded by ASTContext's own
+ // `if (Decl->TypeForDecl) return`, never overwritten a second
+ // time), so non-null right now IS the confirmation.
+ // if (DeclStateReverter::getRawTypeForDecl(TD))
+ // Verified |= uint32_t(MutationType::TypeForDecl);
+ }
+ } break;
+ case DeclShape::None:
+ break; // never flagged in the first place.
+ }
+ return Verified;
+}
+
+template <typename OnConfirmedFn>
+void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
+ llvm::SmallVector<std::pair<const Decl *, uint32_t>, 8> Confirmed;
+ for (auto &Entry : Active) {
+ const Decl *D = Entry.getFirst();
+ uint32_t StillOpen = Entry.getSecond();
+ DeclShape S = classifyShape(D);
+ if (uint32_t Newly = Act.verifyMutationFor(D, S, StillOpen)) {
+ OnConfirmed(D, S, Newly);
+ Confirmed.emplace_back(D, Newly);
+ }
+ }
+ for (auto &[D, Newly] : Confirmed)
+ settle(D, Newly);
+}
+
+/// Verify recorded mutations and remove any spurious ones. If none of a
+/// decl’s mutation kinds are confirmed, remove the record entirely so
+/// restore() does not pop chain entries that were never written.
+void PTUStateInfo::verifyMutations(PTUMutationActions &Actions) {
+ Mutations.remove_if([&](auto &Entry) -> bool {
+ uint32_t Confirmed = Actions.verifyMutationFor(Entry.first, Entry.second.S,
+ Entry.second.MutationType);
+ Entry.second.MutationType = Confirmed;
+ return Confirmed == 0;
+ });
+}
+
+class DeclStateCommitPolicy {
+ PTUID ID;
+ PTUMutationActions &Action;
+
+public:
+ DeclStateCommitPolicy(PTUID ID, PTUMutationActions &Action)
+ : ID(ID), Action(Action) {}
+ /// Everything shouldAct resolved, carried to the caller instead of
+ /// stashed. Empty means "skip this decl" -- there's no separate bool.
+ struct CommitAction {
+ DeclShape S;
+ uint32_t Kinds;
+ explicit operator bool() const { return Kinds != 0; }
+ };
+
+ bool shouldRecurse(const Decl *D) const {
+ if (isa<NamespaceDecl>(D))
+ return true;
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(D))
+ return RD->isCompleteDefinition();
+ return false;
+ }
+
+ CommitAction actionFor(const Decl *D) const {
+ if (!D->isDefinedOutsideFunctionOrMethod())
+ return {};
+
+ DeclShape S = classifyShape(D);
+ if (S == DeclShape::None)
+ return {};
+
+ // we have to handle every case that if a decl need to create baseline
+ // info or if any decl has done all mutations already in this for exmpla
+ // membe spec already properly done like this kind of case so we don't
+ // create spec info blindly
+ uint32_t Kinds = Action.kindsNeedingTracking(S, D);
+ if (!Kinds)
+ return {}; // structurally immutable -- no baseline needed
+
+ return CommitAction{S, Kinds};
+ }
+
+ void runAction(const Decl *D, const CommitAction &A) {
+ MutationRecord Rec = MutationRecord{A.S, A.Kinds};
+ Action.commitDecl(ID, D, Rec, /*IsNew=*/true);
+ }
+};
+
+class DeclStateUnlinkPolicy {
+ PTUID ID;
+ IncrementalStateTracker &Tracker;
+ DeclStateReverter &Reverter;
+ llvm::SmallPtrSet<const Decl *, 16> RepairedChains;
+ llvm::SmallPtrSet<const DeclContext *, 8> RepairedLexicalContexts;
+ llvm::SmallPtrSet<const DeclContext *, 8> TouchedDC;
+
+public:
+ DeclStateUnlinkPolicy(PTUID ID, IncrementalStateTracker &Tracker,
+ DeclStateReverter &Reverter)
+ : ID(ID), Tracker(Tracker), Reverter(Reverter) {}
+
+ struct RestoreAction {
+ bool DetachFromLexicalChain = false;
+ bool DetachFromDC = false;
+ bool DetachFromRedecl = false;
+ bool DetachDefData = false;
+ // bool DetachCommonBase = false;
+ bool HasTemplated = false;
+ bool TemplatedDetachDefData = false;
+ explicit operator bool() const {
+ return DetachFromLexicalChain || DetachFromDC || DetachFromRedecl ||
+ DetachDefData || /*DetachCommonBase ||*/ HasTemplated ||
+ TemplatedDetachDefData;
+ }
+ };
+
+ bool shouldRecurse(const Decl *D) const {
+ // we don't care about the decl which complete chain is part belong to
+ // current PTU.
+ if (Tracker.isFromThisPTU(D->getCanonicalDecl(), ID))
+ return false;
+ return isa<NamespaceDecl>(D);
+ }
+
+ RestoreAction actionFor(const Decl *D) const {
+ RestoreAction A;
+ const Decl *Canon = D->getCanonicalDecl();
+
+ /// Keyed on the canonical decl: every redeclaration shares one
+ /// RedeclLink, so truncating twice would step past the intended survivor.
+ if (!RepairedChains.count(Canon) && D->getPreviousDecl() &&
+ !Tracker.isFromThisPTU(Canon, ID))
+ A.DetachFromRedecl = true;
+
+ // Keyed on the primary context, a different granularity: a reopened
+ // namespace's redeclarations all resolve to the same primary.
+ const DeclContext *DC = D->getDeclContext()->getPrimaryContext();
+ if (TouchedDC.contains(DC))
+ A.DetachFromDC = true;
+
+ if (D->isImplicit() &&
+ !RepairedLexicalContexts.count(D->getLexicalDeclContext()))
+ A.DetachFromLexicalChain = true;
+
+ if (const auto *RT = dyn_cast<RedeclarableTemplateDecl>(D)) {
+ // if (Tracker.isFromThisPTU(::needToDetachCommonPtr(CheckPoint, RT),
+ // ID))
+ // A.DetachCommonBase = true;
+
+ const NamedDecl *T = RT->getTemplatedDecl();
+ A.HasTemplated = true;
+ if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(T)) {
+ if (const CXXRecordDecl *Def = RD->getDefinition();
+ Def && Tracker.isFromThisPTU(Def, ID))
+ A.TemplatedDetachDefData = true;
+ }
+ } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
+ if (const CXXRecordDecl *Def = RD->getDefinition();
+ Def && Tracker.isFromThisPTU(Def, ID))
+ A.DetachDefData = true;
+ }
+
+ return A;
+ }
+
+ void runAction(const Decl *D, const RestoreAction &A) {
+ if (A.DetachDefData)
+ Reverter.detachDefData(D);
+ // if (A.DetachCommonBase)
+ // Detacher.detachCommonBase(D);
+ if (A.DetachFromDC)
+ Reverter.detachFromDCLookup(const_cast<Decl *>(D), ID);
+ if (A.DetachFromRedecl) {
+ RepairedChains.insert(D->getCanonicalDecl());
+ Reverter.detachFromRedeclChain(D, ID);
+ }
+
+ if (A.HasTemplated) {
+ const Decl *T = cast<RedeclarableTemplateDecl>(D)->getTemplatedDecl();
+ if (A.TemplatedDetachDefData)
+ Reverter.detachDefData(T);
+ if (A.DetachFromRedecl)
+ Reverter.detachFromRedeclChain(T, ID);
+ }
+
+ if (A.DetachFromLexicalChain) {
+ Reverter.repairLexicalChain(
+ *const_cast<DeclContext *>(D->getLexicalDeclContext()), ID);
+ RepairedLexicalContexts.insert(D->getLexicalDeclContext());
+ }
+ }
+};
+
+template <typename DeclStateProxyT>
+void PTUMutationActions::walkDecls(const DeclContext *DC,
+ DeclStateProxyT &Proxy) {
+ for (const Decl *D : DC->decls()) {
+ if (Proxy.shouldRecurse(D))
+ walkDecls(cast<DeclContext>(D), Proxy);
+ if (auto Act = Proxy.actionFor(D))
+ Proxy.runAction(D, Act);
+ }
+}
+
+/// -----------------------------------------------------------------------
+//////////////////////// PTUMutationActions::restore //////////////////////
+/// -----------------------------------------------------------------------
+
+void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
+ MutationRecord &Rec) {
+
+ // DefinitionArrived and DefinitionDataChanged commit identically -- both
+ // mean "this record's DefinitionData needs a fresh footprint." They stay
+ // distinct kinds because rollback and dependency-edge attribution treat
+ // them differently (arrival is this PTU completing a record that
+ // predates it; a data change may be either).
+ if (Rec.has(MutationType::DefinitionInstantiate) ||
+ Rec.has(MutationType::DefinitionData)) {
+ // CXXRecordDecl *RD = cast<CXXRecordDecl>(RD);
+ // New: no prior entry exists, so write unconditionally.
+ // Existing: only write a real diff.
+ /// SpecializationDecl can have lazy implicit generated body;
+ if (Rec.has(MutationType::DefinitionInstantiate)) {
+ DeclStateReverter::revertDefinitionArrival(
+ *const_cast<CXXRecordDecl *>(RD));
+ } else {
+ auto *Chain = Tracker.getChainFor(RD);
+ if (Chain) {
+ if (const DefinitionDataFootprint *Restored = Chain->mostRecent())
+ DeclStateReverter::restoreDefinitionDataFootprint(
+ *Restored, *const_cast<CXXRecordDecl *>(RD));
+ }
+ }
+ }
+
+ // TypeForDecl is shared across the whole redeclaration chain -- revert all.
+ if (Rec.has(MutationType::TypeForDecl)) {
+ // CXXRecordDecl *RD = cast<CXXRecordDecl>(D);
+ for (const TagDecl *Redecl : RD->redecls()) {
+ const Type *LastKnown =
+ Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+ const TypeDecl *TD = cast<TypeDecl>(Redecl);
+ if (LastKnown != TD->getTypeForDecl())
+ const_cast<TypeDecl *>(TD)->setTypeForDecl(LastKnown);
+ }
+ }
+
+ if (Rec.has(MutationType::SpecInfo)) {
+ // Only a specialization can carry this kind; a plain CXXRecordDecl
+ // reaching here means a note*() wrapper passed the wrong kind.
+ const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD);
+ assert(Spec && "SpecializationAdded noted on a non-specialization");
+ if (auto *Chain = Tracker.getChainFor(Spec)) {
+ if (const SpecializationFootprint *Restored = Chain->mostRecent())
+ DeclStateReverter::restoreSpecializationFootprint(
+ *Restored, *const_cast<ClassTemplateSpecializationDecl *>(Spec));
+ }
+ }
+
+ if (Rec.has(MutationType::MemberSpecInfo)) {
+ // A nested member of a class template – its MSI kind or
+ // point-of-instantiation changed.
+ assert(RD->getMemberSpecializationInfo());
+ if (auto *Chain = Tracker.getMemberSpecChainFor(RD)) {
+ if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
+ DeclStateReverter::restoreMemberSpecializationFootprint(
+ *Restored, *const_cast<CXXRecordDecl *>(RD));
+ }
+ }
+}
+
+void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
+ MutationRecord &Rec) {
+
+ // FunctionType is shared across the whole redeclaration chain.
+ // Resolving a deferred noexcept or deducing an auto return updates the
+ // type for every redecl, including ones owned by earlier PTUs, so each
+ // redecl needs to be recorded rather than just FD’s own.
+ if (Rec.has(MutationType::ExceptionSpec) ||
+ Rec.has(MutationType::DeducedReturnType)) {
+ for (const FunctionDecl *Redecl : FD->redecls()) {
+ QualType LastKnown =
+ Tracker.getFunctionTypeMutations().mostRecent(Redecl).value_or(
+ QualType());
+ if (LastKnown != Redecl->getType())
+ const_cast<FunctionDecl *>(Redecl)->setType(LastKnown);
+ }
+ }
+
+ if (Rec.has(MutationType::DefinitionInstantiate))
+ DeclStateReverter::revertDefinitionArrival(*const_cast<FunctionDecl *>(FD));
+
+ if (Rec.has(MutationType::SpecInfo)) {
+ // A function template specialization is a plain FunctionDecl carrying
+ // FunctionTemplateSpecializationInfo.
+ assert(FD->getTemplateSpecializationKind() != TSK_Undeclared &&
+ "SpecInfoChanged on a function with no specialization info");
+ if (auto *Chain = Tracker.getChainFor(FD)) {
+ if (const FunctionSpecializationFootprint *Restored = Chain->mostRecent())
+ DeclStateReverter::restoreFunctionSpecializationFootprint(
+ *Restored, *const_cast<FunctionDecl *>(FD));
+ }
+ }
+
+ if (Rec.has(MutationType::MemberSpecInfo)) {
+ assert(FD->getMemberSpecializationInfo());
+ if (auto *Chain = Tracker.getMemberSpecChainFor(FD)) {
+ if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
+ DeclStateReverter::restoreMemberSpecializationFootprint(
+ *Restored, *const_cast<FunctionDecl *>(FD));
+ }
+ }
+}
+
+void PTUMutationActions::restoreTemplate(PTUID ID,
+ const RedeclarableTemplateDecl *TD,
+ MutationRecord &Rec) {
+
+ if (Rec.has(MutationType::TemplateCommon)) {
+ // TODO: call DeclStateReverter to detech all decl's common ptr if canon
+ // decl of template ie being tracked;
+ // DeclStateReverter::resetTemplateCommonBase(
+ // *const_cast<RedeclarableTemplateDecl *>(TD));
+ DeclStateReverter::detachCommonBase(TD);
+ // Common’s creation was done by this PTU.
+ // Re-register both bits, since a later PTU may trigger either one again,
+ // and CanonInjectedTST cannot exist until Common exists again first.
+ //
+ // TODO: Track only the canonical decl.
+ HiddenMutationTracker.track(TD,
+ uint32_t(MutationType::TemplateCommon) |
+ uint32_t(MutationType::CanonInjectedTST));
+ } else if (Rec.has(MutationType::CanonInjectedTST)) {
+ if (const auto *CTD = dyn_cast<ClassTemplateDecl>(TD)) {
+ DeclStateReverter::resetCanonInjectedTST(
+ *const_cast<ClassTemplateDecl *>(CTD));
+ HiddenMutationTracker.track(TD, uint32_t(MutationType::CanonInjectedTST));
+ }
+ }
+
+ if (Rec.has(MutationType::SpecializationAdded))
+ DeclStateReverter::removeSpecializations(Tracker.getPTUSlabCheckpoints(),
+ TD, ID);
+}
+
+void PTUMutationActions::restoreTypedef(PTUID ID, const TypedefNameDecl *TD,
+ MutationRecord &Rec) {
+ if (Rec.has(MutationType::TypeForDecl)) {
+ // TD survives this rollback (only the cached Type was this PTU's
+ // doing) -- reset the live field and re-register: some later PTU
+ // could re-trigger ASTContext::getTypedefType's first-call write.
+ // DeclStateReverter::resetTypeForDecl(const_cast<TypedefNameDecl *>(TD));
+ // HiddenMutationTracker.track(TD, uint32_t(MutationType::TypeForDecl));
+ }
+}
+
+void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
+ MutationRecord &Rec) {
+
+ using VarMutation = MutationType;
+
+ // EvaluatedStmt::WasEvaluated is write-once, so its previous state is always
+ // null here. If it made it into Rec, this PTU had confirmed it as evaluated.
+ // Reverting it is therefore just a reset, not a snapshot restore – the
+ // original initializer expression in Eval->Value stays untouched; we only
+ // clear the cached result.
+ if (Rec.has(VarMutation::EvaluatedValue)) {
+ // VD itself survives this rollback (only its cached value was this
+ // PTU's doing) -- reset the live field, and re-register with
+ // SweepTracker since kindsNeedingTracking will flag this as open
+ // again and some later PTU could re-trigger evaluation.
+ if (EvaluatedStmt *Eval = VD->getEvaluatedStmt()) {
+ Eval->WasEvaluated = false;
+ Eval->Evaluated = APValue();
+ }
+ HiddenMutationTracker.track(VD, uint32_t(VarMutation::EvaluatedValue));
+ }
+
+ if (Rec.has(VarMutation::DefinitionInstantiate)) {
+ /// TODO;
+ DeclStateReverter::revertDefinitionArrival(*const_cast<VarDecl *>(VD));
+ }
+
+ if (Rec.has(VarMutation::SpecInfo)) {
+ // Unlike functions, a variable specialization IS a distinct type.
+ const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
+ if (auto *Chain = Tracker.getChainFor(Spec)) {
+ if (const VarSpecializationFootprint *Restored = Chain->mostRecent())
+ DeclStateReverter::restoreVarSpecializationFootprint(
+ *Restored, *const_cast<VarTemplateSpecializationDecl *>(Spec));
+ }
+ }
+
+ if (Rec.has(VarMutation::MemberSpecInfo)) {
+ assert(VD->getMemberSpecializationInfo());
+ if (auto *Chain = Tracker.getMemberSpecChainFor(VD)) {
+ if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
+ DeclStateReverter::restoreMemberSpecializationFootprint(
+ *Restored, *const_cast<VarDecl *>(VD));
+ }
+ }
+}
+
+void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
+ MutationRecord &Rec) {
+
+ // Like a class, an EnumDecl's TypeForDecl is shared across its whole
+ // redeclaration chain (`enum class E : int;` forward-declared, then
+ // defined later), so every redecl's cached pointer needs checking.
+ if (Rec.has(MutationType::TypeForDecl)) {
+ for (const TagDecl *Redecl : ED->redecls()) {
+ const Type *LastKnown =
+ Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+ const TypeDecl *TD = cast<TypeDecl>(Redecl);
+ if (LastKnown != TD->getTypeForDecl())
+ const_cast<TypeDecl *>(TD)->setTypeForDecl(LastKnown);
+ }
+ }
+
+ if (Rec.has(MutationType::DefinitionInstantiate))
+ DeclStateReverter::revertDefinitionArrival(*const_cast<EnumDecl *>(ED));
+
+ if (Rec.has(MutationType::MemberSpecInfo)) {
+ // A scoped member enumeration of a class template -- instantiated with
+ // the enclosing specialization, carrying MSI back to the pattern enum.
+ assert(ED->getMemberSpecializationInfo());
+ if (auto *Chain = Tracker.getMemberSpecChainFor(ED)) {
+ if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
+ DeclStateReverter::restoreMemberSpecializationFootprint(
+ *Restored, *const_cast<EnumDecl *>(ED));
+ }
+ }
+}
+
+void PTUMutationActions::restoreDecl(PTUID ID, const Decl *D,
+ MutationRecord &Rec) {
+ switch (Rec.S) {
+ case DeclShape::Class:
+ restoreClass(ID, cast<CXXRecordDecl>(D), Rec);
+ break;
+ case DeclShape::Function:
+ restoreFunction(ID, cast<FunctionDecl>(D), Rec);
+ break;
+ case DeclShape::Var:
+ restoreVar(ID, cast<VarDecl>(D), Rec);
+ break;
+ case DeclShape::Enum:
+ restoreEnum(ID, cast<EnumDecl>(D), Rec);
+ break;
+ case DeclShape::Template:
+ restoreTemplate(ID, cast<RedeclarableTemplateDecl>(D), Rec);
+ break;
+ case DeclShape::Typedef:
+ restoreTypedef(ID, cast<TypedefNameDecl>(D), Rec);
+ break;
+ case DeclShape::None:
+ break;
+ }
+}
+
+void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
+ PTUStateInfo &Cur = Tracker.current();
+ assert(ThisTU == Cur.ThisPTU);
+ // TranslationUnitDecl *ThisTU = Cur.ThisPTU;
+ PTUID ID = Cur.ID;
+ if (Cur.Commited)
+ Tracker.undoLastEntries();
+ else {
+ // TODO: verify Mutations;
+ Cur.verifyMutations(*this);
+ Tracker.getHiddenMutationTracker().sweep(
+ *this, [&](const Decl *D, DeclShape S, uint32_t K) {
+ Cur.noteMutated(D, S, K);
+ });
+ }
+
+ for (auto &[D, Rec] : Cur.Mutations)
+ restoreDecl(ID, D, Rec);
+
+ DeclStateReverter Detacher(Tracker.getPTUSlabCheckpoints());
+ DeclStateUnlinkPolicy Proxy(ID, Tracker, Detacher);
+
+ for (const Decl *D : Cur.ImplicitDecls)
+ if (auto A = Proxy.actionFor(D))
+ Proxy.runAction(D, A);
+
+ walkDecls(ThisTU, Proxy);
+
+ // Sema::SpecialMemberCache (public) caches, per (RD, kind+qualifiers), the
+ // CXXMethodDecl* a prior LookupSpecialMember() call resolved to -- and on
+ // a cache hit it returns that decl directly, with no re-validation at all
+ // (SemaLookup.cpp). Any entry whose cached method was created by the PTU
+ // being rolled back is now stale, so purge it here rather than trying to
+ // reconstruct which of the (RD, kind) keys it could occupy.
+ {
+ Sema &SemaRef = Tracker.getSema();
+ llvm::SmallVector<Sema::SpecialMemberCacheKey, 8> Stale;
+ for (auto &Entry : SemaRef.SpecialMemberCache) {
+ CXXMethodDecl *MD = Entry.second.getMethod();
+ if (MD &&
+ (Tracker.isFromThisPTU(MD, ID) || Cur.ImplicitDecls.contains(MD)))
+ Stale.push_back(Entry.first);
+ }
+ for (const auto &Key : Stale)
+ SemaRef.SpecialMemberCache.erase(Key);
+ }
+
+ // Must be last: Cur/ID/ThisTU are all references into (or derived from)
+ // the PTU this call retires, and popCurrentPTU() destroys that entry.
+ // Runs for BOTH branches above (committed or not) -- a PTU that never
+ // committed still needs its checkpoint-ledger slot and PTUStack
+ // entry reclaimed, otherwise every failed input would permanently grow
+ // both, and NextID would desync from PTUSlabCheckpoints on top of that.
+ Tracker.popCurrentPTU();
+}
+
+/// -----------------------------------------------------------------------
+////////////////////////// MutationListener ///////////////////////////////
+/// -----------------------------------------------------------------------
+
+void PTUMutationRecorder::CompletedTagDefinition(const TagDecl *D) {
+ if (!D->isDefinedOutsideFunctionOrMethod())
+ return; // ordinary local class -- structurally unreachable by any other
+ // PTU
+
+ PTUStateInfo &Cur = Tracker.current();
+ if (Tracker.isFromThisPTU(D, Tracker.currentID())) {
+ if (D->isImplicit())
+ Cur.ImplicitDecls.insert(D);
+ return;
+ }
+ // Predates this PTU: a genuine cross-PTU mutation -- needs the full
+ // definition-data footprint captured in commit().
+ // Cur.TouchedTagDecls.insert(D);
+ DeclShape S = classifyShape(D);
+
+ /// doing this for memspec because we are not sure that if this cause member
+ /// spec mutation or not we can't put compare check here as well since
+ /// notifer never guarranty that will called before mutation or later. so
+ /// this check about mutation will be handled in verification layer
+ Cur.noteMutated(D, S, MutationType::DefinitionInstantiate);
+ if (S == DeclShape::Enum) {
+ if (const EnumDecl *ED = dyn_cast<EnumDecl>(D);
+ ED && ED->getMemberSpecializationInfo())
+ Cur.noteMutated(D, S, MutationType::MemberSpecInfo);
+ } else if (S == DeclShape::Class) {
+ if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D);
+ RD && RD->getMemberSpecializationInfo())
+ Cur.noteMutated(D, S, MutationType::MemberSpecInfo);
+ if (isa<ClassTemplateSpecializationDecl>(D))
+ Cur.noteMutated(D, S, MutationType::SpecInfo);
+ }
+}
+
+void PTUMutationRecorder::AddedVisibleDecl(const DeclContext *DC,
+ const Decl *D) {
+ PTUStateInfo &Cur = Tracker.current();
+ if (D->isImplicit())
+ Cur.ImplicitDecls.insert(D);
+
+ if (Tracker.isFromThisPTU(DC, Cur.ID) || Cur.TouchedDC.contains(DC))
+ return;
+ // auto Origin =
+ // Tracker.getPTUSlabCheckpoints().attribute(Tracker.Ctx, DC);
+ // assert(Origin && "must have existing PTUID");
+ // assert(*Origin != Cur.ID);
+ Cur.TouchedDC.insert(DC);
+}
+
+void PTUMutationRecorder::AddedCXXImplicitMember(const CXXRecordDecl *RD,
+ const Decl *D) {
+ if (!RD->isDefinedOutsideFunctionOrMethod())
+ return; // ordinary local class -- structurally unreachable by any other
+ // PTU
+
+ PTUStateInfo &Cur = Tracker.current();
+
+ if (D->isImplicit())
+ Cur.ImplicitDecls.insert(D);
+ // cover this part as well
+ // if (RD->MemberInfo and D is implcit plus has MSIInfo)
+
+ // cover only not from this ptu.
+ if (Tracker.isFromThisPTU(RD, Cur.ID))
+ return;
+
+ Cur.noteMutated(RD, DeclShape::Class, MutationType::DefinitionData);
+}
+
+template <typename TemplateT, typename SpecT>
+void PTUMutationRecorder::noteTemplateDeclMutation(const TemplateT *TD,
+ const SpecT *Spec,
+ DeclShape S) {
+ PTUStateInfo &Cur = Tracker.current();
+
+ if (Spec->isImplicit())
+ Cur.ImplicitDecls.insert(Spec);
+
+ if (Tracker.isFromThisPTU(TD, Cur.ID))
+ return;
+
+ Cur.noteMutated(TD, DeclShape::Template, MutationType::SpecializationAdded);
+}
+
+void PTUMutationRecorder::AddedCXXTemplateSpecialization(
+ const ClassTemplateDecl *TD, const ClassTemplateSpecializationDecl *D) {
+ noteTemplateDeclMutation(TD, TD, DeclShape::Class);
+}
+
+void PTUMutationRecorder::AddedCXXTemplateSpecialization(
+ const VarTemplateDecl *TD, const VarTemplateSpecializationDecl *D) {
+ noteTemplateDeclMutation(TD, TD, DeclShape::Var);
+}
+
+void PTUMutationRecorder::AddedCXXTemplateSpecialization(
+ const FunctionTemplateDecl *TD, const FunctionDecl *D) {
+ noteTemplateDeclMutation(TD, TD, DeclShape::Function);
+}
+
+void PTUMutationRecorder::noteExceptionSpecMutation(const FunctionDecl *FD,
+ MutationType K) {
+ PTUStateInfo &Cur = Tracker.current();
+ if (Tracker.isFromThisPTU(FD, Cur.ID))
+ return;
+ Cur.noteMutated(FD, DeclShape::Function, K);
+}
+
+void PTUMutationRecorder::ResolvedExceptionSpec(const FunctionDecl *FD) {
+ noteExceptionSpecMutation(FD);
+}
+
+void PTUMutationRecorder::DeducedReturnType(const FunctionDecl *FD,
+ QualType ReturnType) {
+ noteExceptionSpecMutation(FD, MutationType::DeducedReturnType);
+}
+
+void PTUMutationRecorder::noteDefinitionInstantiated(const Decl *D) {
+ PTUStateInfo &Cur = Tracker.current();
+ if (Tracker.isFromThisPTU(D, Cur.ID)) {
+ return;
+ }
+ Cur.noteMutated(D, classifyShape(D), MutationType::DefinitionInstantiate);
+}
+
+/// InstantiationRequested fires when Sema decides a template entity needs
+/// instantiating -- before the instantiation actually happens.
+void PTUMutationRecorder::InstantiationRequested(const ValueDecl *D) {
+ PTUStateInfo &Cur = Tracker.current();
+
+ if (Tracker.isFromThisPTU(D, Cur.ID)) {
+ // Created this PTU -- the creation path covers it, no mutation to note.
+ // But an implicitly-created decl may never be appended to any lexical
+ // chain, so walkTU would never see it. Record it here as the
+ // only path that will.
+ if (D->isImplicit())
+ Cur.ImplicitDecls.insert(D);
+ return;
+ }
+
+ DeclShape S;
+ MutationType K = MutationType::None;
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ S = DeclShape::Function;
+ if (FD->getMemberSpecializationInfo())
+ K = MutationType::MemberSpecInfo;
+ else if (FD->getTemplateSpecializationKind() != TSK_Undeclared)
+ // A function template specialization is a plain FunctionDecl
+ // carrying FunctionTemplateSpecializationInfo -- there is no
+ // distinct decl type to dyn_cast to, unlike the variable case.
+ K = MutationType::SpecInfo;
+ else
+ return; // neither -- nothing this notifier can be about
+ } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ S = DeclShape::Var;
+ if (VD->getMemberSpecializationInfo())
+ K = MutationType::MemberSpecInfo;
+ else if (isa<VarTemplateSpecializationDecl>(VD))
+ K = MutationType::SpecInfo;
+ else
+ return;
+ } else {
+ return; // no other ValueDecl kind participates in instantiation
+ }
+
+ Cur.noteMutated(D, S, K);
+}
+
+} // end namespace clang
\ No newline at end of file
>From 47213dd55649003c79ce6effc0904b6cd8877892 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Fri, 25 Sep 2026 10:28:42 +0530
Subject: [PATCH 08/13] Refactor: ErrRecovery.cpp impl
---
.../include/clang/Interpreter/ErrorRecovery.h | 67 ++---
clang/lib/Interpreter/ErrRecovery.cpp | 276 ++++++++++--------
2 files changed, 166 insertions(+), 177 deletions(-)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 61380ec6ea0ad..4569494a3b2e2 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -893,9 +893,6 @@ class LinkedDeclNodeGenerator {
return MemberSpecChains.acquire();
}
- // One release() name per type, dispatched by overload resolution --
- // the caller doesn't need to know which internal pool a given pointer
- // belongs to.
void release(CXXClassDeclNode *N) { RecordNodes.release(N); }
void release(VarDeclNode *N) { VarNodes.release(N); }
void release(FunctionDeclNode *N) { FunctionNodes.release(N); }
@@ -1192,58 +1189,28 @@ class PTUMutationActions {
HiddenMutationTracker(Tracker.getHiddenMutationTracker()),
Reverter(Tracker.getPTUSlabCheckpoints()) {}
- template <typename DeclStateProxyT>
- void walkDecls(const DeclContext *DC, DeclStateProxyT &Proxy);
+ template <typename DeclStatePolicyT>
+ void walkDecls(const DeclContext *DC, DeclStatePolicyT &Proxy);
- uint32_t kindsNeedingTracking(DeclShape S, const Decl *D);
+ uint32_t DeclNeedingTracking(DeclShape S, const Decl *D);
uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
private:
- /// Every mutation this PTU recorded for a class-shaped decl, applied in
- /// dependency order: definition data first (later steps read the completed
- /// definition), then the type cache, then specialization footprints, then
- /// lazily-completed members last (they can add members that the steps above
- /// would otherwise have missed).
- ///
- /// Kinds are a bitmask, not alternatives -- one class can legitimately have
- /// several set in a single PTU (e.g. a specialization that also had its
- /// definition data completed), so these are sequential checks, not a switch.
- void commitClassFamily(PTUID ID, const CXXRecordDecl *RD, MutationRecord &Rec,
- bool IsNew);
-
- /// Every mutation this PTU recorded for a function-shaped decl, or -- when
- /// CR is non-null -- the baseline seed for one this PTU created.
- ///
- /// Kinds are a bitmask, not alternatives: one function can have its
- /// exception spec resolved AND its body instantiated in the same PTU, so
- /// these are sequential checks rather than a switch.
- ///
- /// Order matters: type-affecting mutations (exception spec, deduced return)
- /// come first because the specialization footprint below reads the
- /// function's type; body instantiation comes last because it can only
- /// happen once everything about the signature is settled.
- void commitFunctionFamily(PTUID ID, const FunctionDecl *FD,
- MutationRecord &Rec, bool IsNew);
-
- /// Every mutation this PTU recorded for a var-shaped decl, or -- when CR is
- /// non-null -- the baseline seed for one this PTU created.
- ///
- /// Ordering: initializer instantiation first (it produces the expression
- /// that constant evaluation later consumes), then the cached evaluated
- /// value, then specialization/member footprints which read both.
- void commitVarFamily(PTUID ID, const VarDecl *VD, MutationRecord &Rec,
- bool IsNew);
-
- /// Every mutation this PTU recorded for an enum-shaped decl, or -- when CR
- /// is non-null -- the baseline seed for one this PTU created.
- ///
- /// The smallest family: enums have no specialization category (there is no
- /// such thing as an enum template), no deferred bodies, and no members with
- /// independent mutable state -- EnumConstantDecls live and die with the
- /// EnumDecl, so they are not separately tracked.
- void commitEnumFamily(PTUID ID, const EnumDecl *ED, MutationRecord &Rec,
- bool IsNew);
+ void commitMembers(PTUID ID, const DeclContext *Members);
+
+ void commitClass(PTUID ID, const CXXRecordDecl *RD, MutationRecord &Rec,
+ bool IsNew);
+ void commitFunction(PTUID ID, const FunctionDecl *FD, MutationRecord &Rec,
+ bool IsNew);
+ void commitVar(PTUID ID, const VarDecl *VD, MutationRecord &Rec, bool IsNew);
+
+ void commitEnum(PTUID ID, const EnumDecl *ED, MutationRecord &Rec,
+ bool IsNew);
+ void commitTemplate(PTUID ID, const RedeclarableTemplateDecl *ED,
+ MutationRecord &Rec, bool IsNew);
+ void commitTypedef(PTUID ID, const TypedefNameDecl *ED, MutationRecord &Rec,
+ bool IsNew);
void restoreClass(PTUID ID, const CXXRecordDecl *RD, MutationRecord &Rec);
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index b5070b7f47b7b..216d896f498bd 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -21,6 +21,14 @@
namespace clang {
+#define PTU_SHAPES \
+ PTU_SHAPE(Class, CXXRecordDecl) \
+ PTU_SHAPE(Function, FunctionDecl) \
+ PTU_SHAPE(Var, VarDecl) \
+ PTU_SHAPE(Enum, EnumDecl) \
+ PTU_SHAPE(Template, RedeclarableTemplateDecl) \
+ PTU_SHAPE(Typedef, TypedefNameDecl)
+
/// -----------------------------------------------------------------------
//////////////////////// PTUMutationActions::Helpers ///////////////////////
/// -----------------------------------------------------------------------
@@ -177,11 +185,6 @@ void DeclStateReverter::detachFromDCLookup(Decl *D, PTUID ID) {
}
}
} while (DC->isTransparentContext() && (DC = DC->getParent()));
-
- // DeclContext *Lexical = const_cast<DeclContext
- // *>(ND->getLexicalDeclContext()); if
- // (RepairedLexicalContexts.insert(Lexical).second)
- // repairLexicalChain(*Lexical);
}
// Walk D's redecl chain looking for the newest decl that predates this
@@ -254,17 +257,10 @@ NamedDecl *DeclStateReverter::tryDetachRedeclChain(Decl *D, PTUID ID) {
} break;
case Decl::FunctionTemplate:
- unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
- break;
-
case Decl::TypeAliasTemplate:
- unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
- break;
-
case Decl::VarTemplate:
unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
break;
-
case Decl::Namespace:
unlinkRedeclChain(cast<NamespaceDecl>(D));
break;
@@ -307,7 +303,7 @@ static DeclShape classifyShape(const Decl *D) {
///
/// Explicit specializations are terminal, so their SpecInfo/MemberSpecInfo
/// does not need to be tracked.
-uint32_t PTUMutationActions::kindsNeedingTracking(DeclShape S, const Decl *D) {
+uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
switch (S) {
case DeclShape::Class: {
const auto *RD = cast<CXXRecordDecl>(D);
@@ -334,7 +330,6 @@ uint32_t PTUMutationActions::kindsNeedingTracking(DeclShape S, const Decl *D) {
if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization)
Kinds |= uint32_t(MutationType::SpecInfo);
} else if (const auto *MSI = RD->getMemberSpecializationInfo()) {
- // Same terminal state, same enum, reused for MSI's own Kind/POI.
if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
Kinds |= uint32_t(MutationType::MemberSpecInfo);
}
@@ -572,8 +567,7 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
Tracker.getHiddenMutationTracker().isTrackedFor(
VD, uint32_t(MutationType::EvaluatedValue))) {
// No chain to compare against -- write-once field, so presence of
- // WasEvaluated=true right now IS the confirmation, same reasoning
- // as Shape::Template's CommonCreated/CanonInjectedTSTCached above.
+ // WasEvaluated=true right now IS the confirmation.
const EvaluatedStmt *Eval = VD->getEvaluatedStmt();
if (Eval && Eval->WasEvaluated)
Verified |= uint32_t(MutationType::EvaluatedValue);
@@ -624,8 +618,6 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
case DeclShape::Typedef: {
const auto *TD = cast<TypedefNameDecl>(D);
if (FlaggedKinds & uint32_t(MutationType::TypeForDecl) &&
- // Defensive, on top of SweepTracker::sweep()'s own bookkeeping --
- // same reasoning as the Var/Template cases above.
Tracker.getHiddenMutationTracker().isTrackedFor(
TD, uint32_t(MutationType::TypeForDecl))) {
// No chain to compare against -- write-once field (unlike
@@ -644,18 +636,18 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
template <typename OnConfirmedFn>
void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
- llvm::SmallVector<std::pair<const Decl *, uint32_t>, 8> Confirmed;
+ // llvm::SmallVector<std::pair<const Decl *, uint32_t>, 8> Confirmed;
for (auto &Entry : Active) {
const Decl *D = Entry.getFirst();
uint32_t StillOpen = Entry.getSecond();
DeclShape S = classifyShape(D);
if (uint32_t Newly = Act.verifyMutationFor(D, S, StillOpen)) {
OnConfirmed(D, S, Newly);
- Confirmed.emplace_back(D, Newly);
+ // Confirmed.emplace_back(D, Newly);
}
}
- for (auto &[D, Newly] : Confirmed)
- settle(D, Newly);
+ // for (auto &[D, Newly] : Confirmed)
+ // settle(D, Newly);
}
/// Verify recorded mutations and remove any spurious ones. If none of a
@@ -677,13 +669,6 @@ class DeclStateCommitPolicy {
public:
DeclStateCommitPolicy(PTUID ID, PTUMutationActions &Action)
: ID(ID), Action(Action) {}
- /// Everything shouldAct resolved, carried to the caller instead of
- /// stashed. Empty means "skip this decl" -- there's no separate bool.
- struct CommitAction {
- DeclShape S;
- uint32_t Kinds;
- explicit operator bool() const { return Kinds != 0; }
- };
bool shouldRecurse(const Decl *D) const {
if (isa<NamespaceDecl>(D))
@@ -693,27 +678,23 @@ class DeclStateCommitPolicy {
return false;
}
- CommitAction actionFor(const Decl *D) const {
+ void process(const Decl *D) const {
if (!D->isDefinedOutsideFunctionOrMethod())
- return {};
+ return;
DeclShape S = classifyShape(D);
if (S == DeclShape::None)
- return {};
+ return;
// we have to handle every case that if a decl need to create baseline
// info or if any decl has done all mutations already in this for exmpla
// membe spec already properly done like this kind of case so we don't
// create spec info blindly
- uint32_t Kinds = Action.kindsNeedingTracking(S, D);
+ uint32_t Kinds = Action.DeclNeedingTracking(S, D);
if (!Kinds)
- return {}; // structurally immutable -- no baseline needed
-
- return CommitAction{S, Kinds};
- }
+ return; // structurally immutable -- no baseline needed
- void runAction(const Decl *D, const CommitAction &A) {
- MutationRecord Rec = MutationRecord{A.S, A.Kinds};
+ MutationRecord Rec{S, Kinds};
Action.commitDecl(ID, D, Rec, /*IsNew=*/true);
}
};
@@ -731,21 +712,6 @@ class DeclStateUnlinkPolicy {
DeclStateReverter &Reverter)
: ID(ID), Tracker(Tracker), Reverter(Reverter) {}
- struct RestoreAction {
- bool DetachFromLexicalChain = false;
- bool DetachFromDC = false;
- bool DetachFromRedecl = false;
- bool DetachDefData = false;
- // bool DetachCommonBase = false;
- bool HasTemplated = false;
- bool TemplatedDetachDefData = false;
- explicit operator bool() const {
- return DetachFromLexicalChain || DetachFromDC || DetachFromRedecl ||
- DetachDefData || /*DetachCommonBase ||*/ HasTemplated ||
- TemplatedDetachDefData;
- }
- };
-
bool shouldRecurse(const Decl *D) const {
// we don't care about the decl which complete chain is part belong to
// current PTU.
@@ -754,68 +720,59 @@ class DeclStateUnlinkPolicy {
return isa<NamespaceDecl>(D);
}
- RestoreAction actionFor(const Decl *D) const {
- RestoreAction A;
+ void process(const Decl *D) {
const Decl *Canon = D->getCanonicalDecl();
/// Keyed on the canonical decl: every redeclaration shares one
/// RedeclLink, so truncating twice would step past the intended survivor.
- if (!RepairedChains.count(Canon) && D->getPreviousDecl() &&
- !Tracker.isFromThisPTU(Canon, ID))
- A.DetachFromRedecl = true;
+ bool DetachFromRedecl = !RepairedChains.count(Canon) &&
+ D->getPreviousDecl() &&
+ !Tracker.isFromThisPTU(Canon, ID);
// Keyed on the primary context, a different granularity: a reopened
// namespace's redeclarations all resolve to the same primary.
const DeclContext *DC = D->getDeclContext()->getPrimaryContext();
- if (TouchedDC.contains(DC))
- A.DetachFromDC = true;
+ bool DetachFromDC = TouchedDC.contains(DC);
- if (D->isImplicit() &&
- !RepairedLexicalContexts.count(D->getLexicalDeclContext()))
- A.DetachFromLexicalChain = true;
+ bool DetachFromLexicalChain =
+ D->isImplicit() &&
+ !RepairedLexicalContexts.count(D->getLexicalDeclContext());
+ bool DetachDefData = false;
+ bool HasTemplated = false;
+ bool TemplatedDetachDefData = false;
if (const auto *RT = dyn_cast<RedeclarableTemplateDecl>(D)) {
- // if (Tracker.isFromThisPTU(::needToDetachCommonPtr(CheckPoint, RT),
- // ID))
- // A.DetachCommonBase = true;
-
const NamedDecl *T = RT->getTemplatedDecl();
- A.HasTemplated = true;
+ HasTemplated = true;
if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(T)) {
if (const CXXRecordDecl *Def = RD->getDefinition();
Def && Tracker.isFromThisPTU(Def, ID))
- A.TemplatedDetachDefData = true;
+ TemplatedDetachDefData = true;
}
} else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
if (const CXXRecordDecl *Def = RD->getDefinition();
Def && Tracker.isFromThisPTU(Def, ID))
- A.DetachDefData = true;
+ DetachDefData = true;
}
- return A;
- }
-
- void runAction(const Decl *D, const RestoreAction &A) {
- if (A.DetachDefData)
+ if (DetachDefData)
Reverter.detachDefData(D);
- // if (A.DetachCommonBase)
- // Detacher.detachCommonBase(D);
- if (A.DetachFromDC)
+ if (DetachFromDC)
Reverter.detachFromDCLookup(const_cast<Decl *>(D), ID);
- if (A.DetachFromRedecl) {
- RepairedChains.insert(D->getCanonicalDecl());
+ if (DetachFromRedecl) {
+ RepairedChains.insert(Canon);
Reverter.detachFromRedeclChain(D, ID);
}
- if (A.HasTemplated) {
+ if (HasTemplated) {
const Decl *T = cast<RedeclarableTemplateDecl>(D)->getTemplatedDecl();
- if (A.TemplatedDetachDefData)
+ if (TemplatedDetachDefData)
Reverter.detachDefData(T);
- if (A.DetachFromRedecl)
+ if (DetachFromRedecl)
Reverter.detachFromRedeclChain(T, ID);
}
- if (A.DetachFromLexicalChain) {
+ if (DetachFromLexicalChain) {
Reverter.repairLexicalChain(
*const_cast<DeclContext *>(D->getLexicalDeclContext()), ID);
RepairedLexicalContexts.insert(D->getLexicalDeclContext());
@@ -823,14 +780,95 @@ class DeclStateUnlinkPolicy {
}
};
-template <typename DeclStateProxyT>
+template <typename DeclStatePolicyT>
void PTUMutationActions::walkDecls(const DeclContext *DC,
- DeclStateProxyT &Proxy) {
+ DeclStatePolicyT &Policy) {
for (const Decl *D : DC->decls()) {
- if (Proxy.shouldRecurse(D))
- walkDecls(cast<DeclContext>(D), Proxy);
- if (auto Act = Proxy.actionFor(D))
- Proxy.runAction(D, Act);
+ if (Policy.shouldRecurse(D))
+ walkDecls(cast<DeclContext>(D), Policy);
+ Policy.process(D);
+ }
+}
+
+/// -----------------------------------------------------------------------
+//////////////////////// PTUMutationActions::commit ///////////////////////
+/// -----------------------------------------------------------------------
+
+void PTUMutationActions::commitMembers(PTUID ID, const DeclContext *Members) {
+ DeclStateCommitPolicy Policy(ID, *this);
+ walkDecls(Members, Policy);
+}
+
+void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
+ MutationRecord &Rec, bool IsNew) {
+
+ // TypeForDecl is shared across the whole redeclaration chain – the Type
+ // object itself is owned by whichever redecl first materialized it, so
+ // every redecl’s cached pointer needs to be checked, not just RD’s.
+ if (Rec.has(MutationType::TypeForDecl)) {
+ // for (const TagDecl *Redecl : RD->redecls()) {
+ // const Type *LastKnown =
+ // Tracker.TagdeclInfos.mostRecent(Redecl).value_or(nullptr);
+ // TODO: need to handle?
+ // }
+ }
+
+ // DefinitionArrived and DefinitionDataChanged are committed the same way –
+ // both mean “this record’s DefinitionData needs a fresh footprint.” They
+ // remain separate kinds because rollback and dependency-edge attribution
+ // treat them differently: an arrival means this PTU completed a record
+ // that predates it, while a data change may be either.
+ if (Rec.has(MutationType::DefinitionInstantiate) ||
+ Rec.has(MutationType::DefinitionData)) {
+ Tracker.chainFor(RD).commit(
+ ID, DeclStateReverter::createDefinitionDataFootprint(Tracker.Ctx, *RD));
+
+ /// SpecializationDecl can have lazy implicit generated body;
+ if (Rec.has(MutationType::DefinitionInstantiate)) {
+ if (RD->getMemberSpecializationInfo())
+ commitMembers(ID, cast<DeclContext>(RD));
+ }
+ }
+
+ if (Rec.has(MutationType::SpecInfo)) {
+ // Only a specialization can carry this kind; a plain CXXRecordDecl
+ // reaching here means a note*() wrapper passed the wrong kind.
+ const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD);
+ // assert(Spec && "SpecializationAdded noted on a non-specialization");
+ if (Spec) {
+ Tracker.chainFor(Spec).commit(
+ ID,
+ DeclStateReverter::createSpecializationFootprint(Tracker.Ctx, *Spec));
+ // Member declarations are instantiated eagerly with the class
+ // definition regardless of TSK; only their definitions defer. Walk
+ // them now so each member's MemberSpecializationInfo is tracked from
+ // the moment it exists.
+ if (IsNew && Spec->getSpecializationKind() == TSK_ImplicitInstantiation)
+ commitMembers(ID, cast<DeclContext>(Spec));
+ }
+ }
+
+ if (Rec.has(MutationType::MemberSpecInfo)) {
+ // A nested member of a class template -- its MSI's kind or
+ // point-of-instantiation changed.
+ if (RD->getMemberSpecializationInfo())
+ Tracker.memberSpecChainFor(RD).commit(
+ ID, DeclStateReverter::createMemberSpecializationFootprint(
+ Tracker.Ctx, *RD));
+ }
+}
+
+void PTUMutationActions::commitDecl(PTUID ID, const Decl *D,
+ MutationRecord &Rec, bool IsNew) {
+ switch (Rec.S) {
+#define PTU_SHAPE(NAME, TYPE) \
+ case DeclShape::NAME: \
+ commit##NAME(ID, cast<TYPE>(D), Rec, IsNew); \
+ break;
+ PTU_SHAPES
+#undef PTU_SHAPE
+ case DeclShape::None:
+ break;
}
}
@@ -868,13 +906,11 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
// TypeForDecl is shared across the whole redeclaration chain -- revert all.
if (Rec.has(MutationType::TypeForDecl)) {
// CXXRecordDecl *RD = cast<CXXRecordDecl>(D);
- for (const TagDecl *Redecl : RD->redecls()) {
- const Type *LastKnown =
- Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
- const TypeDecl *TD = cast<TypeDecl>(Redecl);
- if (LastKnown != TD->getTypeForDecl())
- const_cast<TypeDecl *>(TD)->setTypeForDecl(LastKnown);
- }
+ // for (const TagDecl *Redecl : RD->redecls()) {
+ // const Type *LastKnown =
+ // Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+ // TODO: need to handle?
+ // }
}
if (Rec.has(MutationType::SpecInfo)) {
@@ -999,7 +1035,7 @@ void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
if (Rec.has(VarMutation::EvaluatedValue)) {
// VD itself survives this rollback (only its cached value was this
// PTU's doing) -- reset the live field, and re-register with
- // SweepTracker since kindsNeedingTracking will flag this as open
+ // SweepTracker since DeclNeedingTracking will flag this as open
// again and some later PTU could re-trigger evaluation.
if (EvaluatedStmt *Eval = VD->getEvaluatedStmt()) {
Eval->WasEvaluated = false;
@@ -1040,13 +1076,11 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
// redeclaration chain (`enum class E : int;` forward-declared, then
// defined later), so every redecl's cached pointer needs checking.
if (Rec.has(MutationType::TypeForDecl)) {
- for (const TagDecl *Redecl : ED->redecls()) {
- const Type *LastKnown =
- Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
- const TypeDecl *TD = cast<TypeDecl>(Redecl);
- if (LastKnown != TD->getTypeForDecl())
- const_cast<TypeDecl *>(TD)->setTypeForDecl(LastKnown);
- }
+ // for (const TagDecl *Redecl : ED->redecls()) {
+ // const Type *LastKnown =
+ // Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+ // TODO: need to handle?
+ // }
}
if (Rec.has(MutationType::DefinitionInstantiate))
@@ -1067,28 +1101,17 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
void PTUMutationActions::restoreDecl(PTUID ID, const Decl *D,
MutationRecord &Rec) {
switch (Rec.S) {
- case DeclShape::Class:
- restoreClass(ID, cast<CXXRecordDecl>(D), Rec);
- break;
- case DeclShape::Function:
- restoreFunction(ID, cast<FunctionDecl>(D), Rec);
- break;
- case DeclShape::Var:
- restoreVar(ID, cast<VarDecl>(D), Rec);
- break;
- case DeclShape::Enum:
- restoreEnum(ID, cast<EnumDecl>(D), Rec);
- break;
- case DeclShape::Template:
- restoreTemplate(ID, cast<RedeclarableTemplateDecl>(D), Rec);
- break;
- case DeclShape::Typedef:
- restoreTypedef(ID, cast<TypedefNameDecl>(D), Rec);
+#define PTU_SHAPE(NAME, TYPE) \
+ case DeclShape::NAME: \
+ restore##NAME(ID, cast<TYPE>(D), Rec); \
break;
+ PTU_SHAPES
+#undef PTU_SHAPE
case DeclShape::None:
break;
}
}
+#undef PTU_SHAPES
void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
PTUStateInfo &Cur = Tracker.current();
@@ -1110,13 +1133,12 @@ void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
restoreDecl(ID, D, Rec);
DeclStateReverter Detacher(Tracker.getPTUSlabCheckpoints());
- DeclStateUnlinkPolicy Proxy(ID, Tracker, Detacher);
+ DeclStateUnlinkPolicy Policy(ID, Tracker, Detacher);
for (const Decl *D : Cur.ImplicitDecls)
- if (auto A = Proxy.actionFor(D))
- Proxy.runAction(D, A);
+ Policy.process(D);
- walkDecls(ThisTU, Proxy);
+ walkDecls(ThisTU, Policy);
// Sema::SpecialMemberCache (public) caches, per (RD, kind+qualifiers), the
// CXXMethodDecl* a prior LookupSpecialMember() call resolved to -- and on
>From 4e00a3208793cdb973be7a2633a14f639523f75d Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Fri, 25 Sep 2026 12:32:10 +0530
Subject: [PATCH 09/13] Improve: Add FootprintStore removal logic and update
implementation
---
.../include/clang/Interpreter/ErrorRecovery.h | 600 +++---------------
clang/lib/Interpreter/ErrRecovery.cpp | 483 +++++++++-----
2 files changed, 426 insertions(+), 657 deletions(-)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 4569494a3b2e2..2c662e9e53017 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -17,6 +17,7 @@
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
+#include <variant>
namespace clang {
class ASTContext;
@@ -264,8 +265,14 @@ class DeclStateReverter {
}
protected:
- static DefinitionDataFootprint *
- createDefinitionDataFootprint(const ASTContext &Ctx, const CXXRecordDecl &RD);
+ // These all return by value now, not an ASTContext-arena-allocated
+ // pointer: footprints live inside FootprintStore's own SmallVector, which
+ // owns them with ordinary C++ lifetime (ctor/dtor/copy), so there's
+ // nothing left for arena allocation to buy -- the arena's job was only
+ // ever to make these survive without anyone having to free them, and a
+ // normally-owned value does that for free too.
+ static DefinitionDataFootprint
+ createDefinitionDataFootprint(const CXXRecordDecl &RD);
static bool compareDefinitionDataFootprint(const DefinitionDataFootprint &FP,
const CXXRecordDecl &RD);
@@ -273,12 +280,10 @@ class DeclStateReverter {
static void restoreDefinitionDataFootprint(const DefinitionDataFootprint &FP,
CXXRecordDecl &RD);
- static SpecializationFootprint *
- createSpecializationFootprint(const ASTContext &Ctx,
- const ClassTemplateSpecializationDecl &Spec) {
- auto *FP =
- new (Ctx, alignof(SpecializationFootprint)) SpecializationFootprint();
- FP->update(Spec);
+ static SpecializationFootprint
+ createSpecializationFootprint(const ClassTemplateSpecializationDecl &Spec) {
+ SpecializationFootprint FP;
+ FP.update(Spec);
return FP;
}
static bool
@@ -294,12 +299,10 @@ class DeclStateReverter {
FP.restore(Spec);
}
- static VarSpecializationFootprint *
- createVarSpecializationFootprint(const ASTContext &Ctx,
- const VarTemplateSpecializationDecl &Spec) {
- auto *FP = new (Ctx, alignof(VarSpecializationFootprint))
- VarSpecializationFootprint();
- FP->update(Spec);
+ static VarSpecializationFootprint
+ createVarSpecializationFootprint(const VarTemplateSpecializationDecl &Spec) {
+ VarSpecializationFootprint FP;
+ FP.update(Spec);
return FP;
}
static bool
@@ -315,12 +318,10 @@ class DeclStateReverter {
FP.restore(Spec);
}
- static FunctionSpecializationFootprint *
- createFunctionSpecializationFootprint(const ASTContext &Ctx,
- const FunctionDecl &FD) {
- auto *FP = new (Ctx, alignof(FunctionSpecializationFootprint))
- FunctionSpecializationFootprint();
- FP->update(FD);
+ static FunctionSpecializationFootprint
+ createFunctionSpecializationFootprint(const FunctionDecl &FD) {
+ FunctionSpecializationFootprint FP;
+ FP.update(FD);
return FP;
}
static bool compareFunctionSpecializationFootprint(
@@ -335,11 +336,10 @@ class DeclStateReverter {
}
template <typename OwnerT>
- static MemberSpecializationFootprint *
- createMemberSpecializationFootprint(const ASTContext &Ctx, const OwnerT &D) {
- auto *FP = new (Ctx, alignof(MemberSpecializationFootprint))
- MemberSpecializationFootprint();
- FP->update(D);
+ static MemberSpecializationFootprint
+ createMemberSpecializationFootprint(const OwnerT &D) {
+ MemberSpecializationFootprint FP;
+ FP.update(D);
return FP;
}
template <typename OwnerT>
@@ -455,60 +455,6 @@ class DeclStateReverter {
NamedDecl *tryDetachRedeclChain(Decl *D, PTUID ID);
};
-template <typename DataT> struct Snapshot {
- PTUID ID;
- DataT *Data;
-};
-
-template <typename DataT> class StateAwareChain {
- llvm::SmallVector<Snapshot<DataT>, 4> History;
-
-public:
- bool empty() const { return History.empty(); }
-
- const DataT *mostRecent() const {
- return History.empty() ? nullptr : History.back().Data;
- }
-
- std::optional<PTUID> mostRecentID() const {
- return History.empty() ? std::nullopt
- : std::optional<PTUID>(History.back().ID);
- }
-
- std::optional<PTUID> oldestID() const {
- return History.empty() ? std::nullopt
- : std::optional<PTUID>(History.front().ID);
- }
-
- void commit(PTUID ID, DataT *Fresh) {
- if (!History.empty() && *History.back().Data == *Fresh)
- return;
- assert(History.back().ID != ID);
- History.push_back(Snapshot<DataT>{ID, Fresh});
- }
-
- // const DataT *getPrevious(PTUID ID) const {
- // for (auto It = History.rbegin(); It != History.rend(); ++It)
- // if (It->ID < ID)
- // return It->Data;
- // return nullptr;
- // }
-
- /// removal: drop every entry with ID >= \p ID.
- void removeFrom(PTUID ID) {
- while (!History.empty() && History.back().ID >= ID)
- History.pop_back(); // no delete.
- }
-};
-
-using RecordDeclDefinitionDataChain = StateAwareChain<DefinitionDataFootprint>;
-using SpecializationChain = StateAwareChain<SpecializationFootprint>;
-using VarSpecializationChain = StateAwareChain<VarSpecializationFootprint>;
-using FunctionSpecializationChain =
- StateAwareChain<FunctionSpecializationFootprint>;
-using MemberSpecializationChain =
- StateAwareChain<MemberSpecializationFootprint>;
-
/// Maps addresses to the PTU that allocated them, by keeping the slab
/// checkpoint taken before each PTU began.
///
@@ -564,89 +510,6 @@ class PTUCheckpointLedger {
}
};
-template <typename ValueT> struct FieldMutation {
- PTUID ID;
- ValueT OldValue;
-};
-
-template <typename OwnerT, typename ValueT> class FieldMutationChain {
- llvm::DenseMap<const OwnerT *, llvm::SmallVector<FieldMutation<ValueT>, 2>>
- Log;
-
-public:
- void noteMutation(PTUID ID, const OwnerT *Owner, ValueT OldValue) {
- auto &Entries = Log[Owner];
- if (!Entries.empty() && Entries.back().ID == ID)
- return;
- Entries.push_back(FieldMutation<ValueT>{ID, OldValue});
- }
-
- std::optional<ValueT> mostRecent(const OwnerT *Owner) const {
- auto It = Log.find(Owner);
- if (It == Log.end() || It->second.empty())
- return std::nullopt;
- return It->second.back().OldValue;
- }
-
- std::optional<PTUID> mostRecentID(const OwnerT *Owner) const {
- auto It = Log.find(Owner);
- if (It == Log.end())
- return std::nullopt;
- auto &History = It->second;
- return History.empty() ? std::nullopt
- : std::optional<PTUID>(History.back().ID);
- }
-
- std::optional<PTUID> oldestID(const OwnerT *Owner) const {
- auto It = Log.find(Owner);
- if (It == Log.end())
- return std::nullopt;
- auto &History = It->second;
- return History.empty() ? std::nullopt
- : std::optional<PTUID>(History.front().ID);
- }
-
- // template <typename FnT> void forEachOwnerSince(PTUID ID, FnT &&Fn) const {
- // for (auto &Entry : Log)
- // if (!Entry.second.empty() && Entry.second.back().ID >= ID)
- // Fn(Entry.first);
- // }
-
- // void removeFrom(PTUID ID) {
- // llvm::SmallVector<OwnerT *> ToErase;
- // for (auto &Entry : Log) {
- // auto &Entries = Entry.second;
- // while (!Entries.empty() && Entries.back().ID >= ID)
- // Entries.pop_back();
- // if (Entries.empty())
- // ToErase.push_back(Entry.first);
- // }
- // for (const OwnerT *O : ToErase)
- // Log.erase(O);
- // }
-
- /// Same trim-then-erase-if-empty as removeFrom(ID), scoped to one owner
- /// instead of sweeping the whole Log -- for callers that already know
- /// exactly which owners this PTU touched (see PTUStateInfo's
- /// TouchedFunctionTypeOwners/TouchedTagdeclOwners) and don't need to
- /// rediscover that by walking every owner ever logged.
- void removeFromOwner(const OwnerT *Owner, PTUID ID) {
- auto It = Log.find(Owner);
- if (It == Log.end())
- return;
- auto &Entries = It->second;
- while (!Entries.empty() && Entries.back().ID >= ID)
- Entries.pop_back();
- if (Entries.empty())
- Log.erase(It);
- }
-
- void forget(const OwnerT *Owner) { Log.erase(Owner); }
-};
-
-using FunctionExceptionSpecChain = FieldMutationChain<FunctionDecl, QualType>;
-using TypeForDeclChain = FieldMutationChain<TagDecl, const Type *>;
-
struct MutationRecord {
enum class DeclShape : uint16_t {
None = 0,
@@ -690,6 +553,58 @@ struct MutationRecord {
using DeclShape = MutationRecord::DeclShape;
using MutationType = MutationRecord::MutationKind;
+using FootprintValue =
+ std::variant<DefinitionDataFootprint, SpecializationFootprint,
+ VarSpecializationFootprint, FunctionSpecializationFootprint,
+ MemberSpecializationFootprint, QualType, const Type *>;
+
+struct FootprintSnapshot {
+ PTUID ID;
+ FootprintValue Data;
+};
+
+class FootprintStore {
+ llvm::DenseMap<std::pair<const Decl *, MutationType>,
+ llvm::SmallVector<FootprintSnapshot, 2>>
+ Store;
+
+public:
+ // Same dedup StateAwareChain::commit() always had: re-committing the
+ // same value for the same owner+kind is a no-op, not a new history entry.
+ template <typename T>
+ void commit(const Decl *Owner, MutationType K, PTUID ID, const T &Fresh) {
+ auto &History = Store[{Owner, K}];
+ if (!History.empty() && std::get<T>(History.back().Data) == Fresh)
+ return;
+ History.push_back(FootprintSnapshot{ID, FootprintValue(Fresh)});
+ }
+
+ // nullptr means either "no history at all" or "history exists but the
+ // most recent entry isn't a T" -- the latter never legitimately happens
+ // for a given (Owner, K) pair, since K determines which alternative was
+ // ever committed there.
+ template <typename T>
+ const T *mostRecent(const Decl *Owner, MutationType K) const {
+ auto It = Store.find({Owner, K});
+ if (It == Store.end() || It->second.empty())
+ return nullptr;
+ return std::get_if<T>(&It->second.back().Data);
+ }
+
+ /// Drop every entry with ID >= \p ID for this (Owner, K), erasing the
+ /// key entirely once its history is empty.
+ void removeFrom(const Decl *Owner, MutationType K, PTUID ID) {
+ auto It = Store.find({Owner, K});
+ if (It == Store.end())
+ return;
+ auto &History = It->second;
+ while (!History.empty() && History.back().ID >= ID)
+ History.pop_back();
+ if (History.empty())
+ Store.erase(It);
+ }
+};
+
struct PTUStateInfo {
PTUID ID;
const TranslationUnitDecl *ThisPTU; // current info
@@ -701,13 +616,13 @@ struct PTUStateInfo {
/// here touched info mean other this belongs to other PTUs;
llvm::SmallPtrSet<const DeclContext *, 4> TouchedDC;
- /// Exactly the decls this PTU's own commit() pushed a footprint-chain
- /// entry for (DefinitionData/Specialization/MemberSpecialization, via
- /// chainFor()/memberSpecChainFor()).
- llvm::SmallPtrSet<const Decl *, 8> TouchedFootprintDecls;
-
- llvm::SmallPtrSet<const FunctionDecl *, 4> TouchedFunctionTypeOwners;
- llvm::SmallPtrSet<const TagDecl *, 4> TouchedTagdeclOwners;
+ /// Every (Owner, Kind) key this PTU's own commit() wrote a FootprintStore
+ /// entry for -- replaces the old TouchedFootprintDecls/
+ /// TouchedFunctionTypeOwners/TouchedTagdeclOwners trio (one set per
+ /// container that used to exist) with one list, since there's now only
+ /// one container. Duplicates are harmless: FootprintStore::removeFrom is
+ /// idempotent.
+ llvm::SmallVector<std::pair<const Decl *, MutationType>, 8> TouchedFootprints;
explicit PTUStateInfo(PTUID ID) : ID(ID) {}
@@ -779,137 +694,10 @@ class SweepTracker {
void sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed);
};
-//===----------------------------------------------------------------------===//
-// DeclLinkedState / LinkedDeclNodeGenerator
-//
-// These nodes keep the footprint-chain state associated with a Decl.
-// Each Decl kind has its own node type so the node only contains state that
-// is valid for that kind. DeclLinkedState then uses the node type to identify
-// which kind of Decl it belongs to.
-//
-// The generator owns these nodes and their chains and reuses them through
-// per-type free lists. This is intentional: nodes need to be released and
-// reused, so they use normal heap allocation instead of ASTContext's
-// bump allocator.
-//
-// The goal is to keep the linked state small, type-safe, and reusable without
-// adding fields for states that a particular Decl can never have.
-//===----------------------------------------------------------------------===//
-struct CXXClassDeclNode {
- PTUID OriginID;
- RecordDeclDefinitionDataChain *DefData = nullptr;
- llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *> Spec;
-};
-
-struct VarDeclNode {
- PTUID OriginID;
- llvm::PointerUnion<VarSpecializationChain *, MemberSpecializationChain *>
- Spec;
-};
-
-struct FunctionDeclNode {
- PTUID OriginID;
- llvm::PointerUnion<FunctionSpecializationChain *, MemberSpecializationChain *>
- Spec;
-};
-
-struct EnumDeclNode {
- PTUID OriginID;
- MemberSpecializationChain *MemberSpec =
- nullptr; // the only thing an enum can ever have
-};
-
-using DeclLinkedState = llvm::PointerUnion<CXXClassDeclNode *, VarDeclNode *,
- FunctionDeclNode *, EnumDeclNode *>;
-
-/// Pool for reusing node/chain objects without requiring any intrusive
-/// free-list field on T itself -- freed pointers are tracked in a side
-/// vector instead, so this works for the four node types above and the
-/// five StateAwareChain-based chain types alike. Released objects are kept
-/// for reuse, and the pool is bounded by Capacity so unused objects do not
-/// cause unbounded memory growth.
-template <typename T> class ObjectPool {
- llvm::SmallVector<T *, 8> Free;
- unsigned Capacity;
-
-public:
- explicit ObjectPool(unsigned Capacity = 64) : Capacity(Capacity) {}
-
- ~ObjectPool() {
- for (T *C : Free)
- delete C;
- }
-
- T *acquire() {
- if (!Free.empty()) {
- T *C = Free.pop_back_val();
- *C = T();
- return C;
- }
- return new T();
- }
- /// Reclaimed for real once Free is at capacity, rather than growing
- /// without bound.
- void release(T *C) {
- if (Free.size() >= Capacity) {
- delete C;
- return;
- }
- Free.push_back(C);
- }
-};
-
-class LinkedDeclNodeGenerator {
- ObjectPool<CXXClassDeclNode> RecordNodes;
- ObjectPool<VarDeclNode> VarNodes;
- ObjectPool<FunctionDeclNode> FunctionNodes;
- ObjectPool<EnumDeclNode> EnumNodes;
-
- ObjectPool<RecordDeclDefinitionDataChain> DefDataChains;
- ObjectPool<SpecializationChain> ClassSpecChains;
- ObjectPool<VarSpecializationChain> VarSpecChains;
- ObjectPool<FunctionSpecializationChain> FunctionSpecChains;
- ObjectPool<MemberSpecializationChain> MemberSpecChains;
-
-public:
- CXXClassDeclNode *acquireRecordNode() { return RecordNodes.acquire(); }
- VarDeclNode *acquireVarNode() { return VarNodes.acquire(); }
- FunctionDeclNode *acquireFunctionNode() { return FunctionNodes.acquire(); }
- EnumDeclNode *acquireEnumNode() { return EnumNodes.acquire(); }
-
- RecordDeclDefinitionDataChain *acquireDefDataChain() {
- return DefDataChains.acquire();
- }
- SpecializationChain *acquireClassSpecChain() {
- return ClassSpecChains.acquire();
- }
- VarSpecializationChain *acquireVarSpecChain() {
- return VarSpecChains.acquire();
- }
- FunctionSpecializationChain *acquireFunctionSpecChain() {
- return FunctionSpecChains.acquire();
- }
- MemberSpecializationChain *acquireMemberSpecChain() {
- return MemberSpecChains.acquire();
- }
-
- void release(CXXClassDeclNode *N) { RecordNodes.release(N); }
- void release(VarDeclNode *N) { VarNodes.release(N); }
- void release(FunctionDeclNode *N) { FunctionNodes.release(N); }
- void release(EnumDeclNode *N) { EnumNodes.release(N); }
- void release(RecordDeclDefinitionDataChain *C) { DefDataChains.release(C); }
- void release(SpecializationChain *C) { ClassSpecChains.release(C); }
- void release(VarSpecializationChain *C) { VarSpecChains.release(C); }
- void release(FunctionSpecializationChain *C) {
- FunctionSpecChains.release(C);
- }
- void release(MemberSpecializationChain *C) { MemberSpecChains.release(C); }
-};
-
-enum class LangMode : uint8_t {
- C, // no DefinitionData, no templates, no implicit special members
- CXX, // full set
-};
+// enum class LangMode : uint8_t {
+// C, // no DefinitionData, no templates, no implicit special members
+// CXX, // full set
+// };
// Overall flow:
// - Track changes made by other PTUs so they can be applied to the relevant
@@ -930,197 +718,36 @@ class IncrementalStateTracker {
// Sema::SpecialMemberCache entries for decls this PTU created.
Sema &SemaRef;
PTUCheckpointLedger PTUSlabCheckpoints;
- LangMode Mode;
PTUID NextID = 0;
mutable std::vector<PTUStateInfo> PTUStack;
- // Stores all declaration footprint state in one map instead of maintaining
- // separate maps for each declaration/specialization kind. The generator
- // creates the appropriate node and chain for each Decl.
- mutable LinkedDeclNodeGenerator Generator;
- mutable llvm::DenseMap<const Decl *, DeclLinkedState> LinkedDecls;
+ // One relation for every tracked footprint/value kind.
+ FootprintStore Footprints;
// Tracks hidden mutations for all supported Decl kinds in one shared tracker,
// so they can be restored correctly during rollback.
SweepTracker HiddenMutationTracker;
- FunctionExceptionSpecChain FunctionTypeMutations;
- TypeForDeclChain TagDeclTypes;
-
friend class PTUMutationActions;
- void noteFunctionTypeMutation(PTUID ID, const FunctionDecl *Owner,
- QualType OldValue) {
- FunctionTypeMutations.noteMutation(ID, Owner, OldValue);
- current().TouchedFunctionTypeOwners.insert(Owner);
- }
- void noteTagDeclTypeMutation(PTUID ID, const TagDecl *Owner,
- const Type *OldValue) {
- TagDeclTypes.noteMutation(ID, Owner, OldValue);
- current().TouchedTagdeclOwners.insert(Owner);
- }
-
- TypeForDeclChain &getTagDeclTypeInfo() { return TagDeclTypes; }
- FunctionExceptionSpecChain &getFunctionTypeMutations() {
- return FunctionTypeMutations;
- }
-
- /// Get-or-create the CXXClassDeclNode backing Canon, acquiring one from
- /// the generator on first use. The only place a CXXClassDeclNode is
- /// created for this map.
- template <typename NodeT, typename AcquireFn>
- NodeT &nodeFor(const Decl *D, AcquireFn Acquire) {
- current().TouchedFootprintDecls.insert(D);
- DeclLinkedState &Info = LinkedDecls[D];
- if (Info.isNull())
- Info = (Generator.*Acquire)();
- return *cast<NodeT *>(Info);
- }
-
- template <typename NodeT> NodeT *lookupNode(const Decl *D) const {
- auto It = LinkedDecls.find(D);
- if (It == LinkedDecls.end() || It->second.isNull())
- return nullptr;
- return dyn_cast<NodeT *>(It->second);
- }
-
- CXXClassDeclNode &recordNodeFor(const CXXRecordDecl *RD) {
- return nodeFor<CXXClassDeclNode>(
- RD, &LinkedDeclNodeGenerator::acquireRecordNode);
- }
- VarDeclNode &varNodeFor(const VarDecl *VD) {
- return nodeFor<VarDeclNode>(VD, &LinkedDeclNodeGenerator::acquireVarNode);
- }
- FunctionDeclNode &functionNodeFor(const FunctionDecl *FD) {
- return nodeFor<FunctionDeclNode>(
- FD, &LinkedDeclNodeGenerator::acquireFunctionNode);
- }
- EnumDeclNode &enumNodeFor(const EnumDecl *ED) {
- return nodeFor<EnumDeclNode>(ED, &LinkedDeclNodeGenerator::acquireEnumNode);
- }
-
- template <typename WantT, typename NodeT, typename AcquireFn>
- WantT &specChain(NodeT &Node, AcquireFn Acquire) {
- if (auto *C = Node.Spec.template dyn_cast<WantT *>())
- return *C;
- assert(Node.Spec.isNull() && "node already holds the other Spec kind");
- auto *C = (Generator.*Acquire)();
- Node.Spec = C;
- return *C;
- }
-
- RecordDeclDefinitionDataChain &chainFor(const CXXRecordDecl *RD) {
- CXXClassDeclNode &Node = recordNodeFor(RD);
- if (!Node.DefData)
- Node.DefData = Generator.acquireDefDataChain();
- return *Node.DefData;
- }
-
- SpecializationChain &chainFor(const ClassTemplateSpecializationDecl *S) {
- return specChain<SpecializationChain>(
- recordNodeFor(cast<CXXRecordDecl>(S)),
- &LinkedDeclNodeGenerator::acquireClassSpecChain);
- }
-
- VarSpecializationChain &chainFor(const VarTemplateSpecializationDecl *S) {
- return specChain<VarSpecializationChain>(
- varNodeFor(cast<VarDecl>(S)),
- &LinkedDeclNodeGenerator::acquireVarSpecChain);
- }
-
- FunctionSpecializationChain &chainFor(const FunctionDecl *S) {
- return specChain<FunctionSpecializationChain>(
- functionNodeFor(cast<FunctionDecl>(S)),
- &LinkedDeclNodeGenerator::acquireFunctionSpecChain);
- }
-
- MemberSpecializationChain &memberSpecChainFor(const FunctionDecl *FD) {
- FunctionDeclNode &Node = functionNodeFor(FD);
- auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
- if (!Chain) {
- Chain = Generator.acquireMemberSpecChain();
- Node.Spec = Chain;
- }
- return *Chain;
- }
- MemberSpecializationChain &memberSpecChainFor(const VarDecl *VD) {
- VarDeclNode &Node = varNodeFor(VD);
- auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
- if (!Chain) {
- Chain = Generator.acquireMemberSpecChain();
- Node.Spec = Chain;
- }
- return *Chain;
- }
- MemberSpecializationChain &memberSpecChainFor(const CXXRecordDecl *RD) {
- CXXClassDeclNode &Node = recordNodeFor(RD);
- auto *Chain = Node.Spec.dyn_cast<MemberSpecializationChain *>();
- if (!Chain) {
- Chain = Generator.acquireMemberSpecChain();
- Node.Spec = Chain;
- }
- return *Chain;
- }
- MemberSpecializationChain &memberSpecChainFor(const EnumDecl *ED) {
- EnumDeclNode &Node = enumNodeFor(ED);
- if (!Node.MemberSpec)
- Node.MemberSpec = Generator.acquireMemberSpecChain();
- return *Node.MemberSpec;
- }
-
- const RecordDeclDefinitionDataChain *
- getChainFor(const CXXRecordDecl *RD) const {
- CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(RD);
- return N ? N->DefData : nullptr;
- }
- const SpecializationChain *
- getChainFor(const ClassTemplateSpecializationDecl *S) const {
- CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(S);
- return N ? N->Spec.dyn_cast<SpecializationChain *>() : nullptr;
- }
-
- const VarSpecializationChain *
- getChainFor(const VarTemplateSpecializationDecl *Spec) const {
- VarDeclNode *N = lookupNode<VarDeclNode>(Spec);
- return N ? N->Spec.dyn_cast<VarSpecializationChain *>() : nullptr;
- }
-
- const FunctionSpecializationChain *
- getChainFor(const FunctionDecl *Spec) const {
- FunctionDeclNode *N = lookupNode<FunctionDeclNode>(Spec);
- return N ? N->Spec.dyn_cast<FunctionSpecializationChain *>() : nullptr;
- }
-
- const MemberSpecializationChain *
- getMemberSpecChainFor(const FunctionDecl *FD) const {
- FunctionDeclNode *N = lookupNode<FunctionDeclNode>(FD);
- return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
- }
-
- const MemberSpecializationChain *
- getMemberSpecChainFor(const VarDecl *VD) const {
- VarDeclNode *N = lookupNode<VarDeclNode>(VD);
- return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
- }
-
- const MemberSpecializationChain *
- getMemberSpecChainFor(const CXXRecordDecl *RD) const {
- CXXClassDeclNode *N = lookupNode<CXXClassDeclNode>(RD);
- return N ? N->Spec.dyn_cast<MemberSpecializationChain *>() : nullptr;
- }
+public:
+ IncrementalStateTracker(ASTContext &Ctx, Sema &S)
+ : Ctx(Ctx), SemaRef(S), PTUSlabCheckpoints(Ctx) {}
- const MemberSpecializationChain *
- getMemberSpecChainFor(const EnumDecl *ED) const {
- EnumDeclNode *N = lookupNode<EnumDeclNode>(ED);
- return N ? N->MemberSpec : nullptr;
+ // Writes a new value and records that this PTU touched (Owner, K), so
+ // undoLastEntries() knows what to remove if the PTU is rolled back
+ // without committing. mostRecent() doesn't need any bookkeeping, so it
+ // reads directly from Footprints via Tracker.Footprints.mostRecent<T>(...)
+ // instead of having its own wrapper.
+ template <typename T>
+ void commitFootprint(const Decl *Owner, MutationType K, PTUID ID,
+ const T &Fresh) {
+ Footprints.commit(Owner, K, ID, Fresh);
+ current().TouchedFootprints.emplace_back(Owner, K);
}
-public:
- IncrementalStateTracker(ASTContext &Ctx, Sema &S, LangMode M)
- : Ctx(Ctx), SemaRef(S), PTUSlabCheckpoints(Ctx), Mode(M) {}
-
- LangMode getMode() const { return Mode; }
+ FootprintStore &getFootprints() { return Footprints; }
PTUStateInfo ¤t() const {
assert(!PTUStack.empty() && "no PTU has been started");
@@ -1156,25 +783,6 @@ class IncrementalStateTracker {
PTUSlabCheckpoints.undoFrom(ID);
NextID = ID;
}
-
- template <typename ChainT>
- static bool removeChainField(ChainT *&Field, PTUID ID,
- LinkedDeclNodeGenerator &Gen);
-
- static bool removeRecordSpec(
- llvm::PointerUnion<SpecializationChain *, MemberSpecializationChain *>
- &Spec,
- PTUID ID, LinkedDeclNodeGenerator &Gen);
- static bool removeVarSpec(
- llvm::PointerUnion<VarSpecializationChain *, MemberSpecializationChain *>
- &Spec,
- PTUID ID, LinkedDeclNodeGenerator &Gen);
- static bool
- removeFunctionSpec(llvm::PointerUnion<FunctionSpecializationChain *,
- MemberSpecializationChain *> &Spec,
- PTUID ID, LinkedDeclNodeGenerator &Gen);
- void removeLinkedDecl(const Decl *D, PTUID ID);
- // just only remove entries from current();
};
class PTUMutationActions {
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index 216d896f498bd..5c61e0165507f 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -33,13 +33,11 @@ namespace clang {
//////////////////////// PTUMutationActions::Helpers ///////////////////////
/// -----------------------------------------------------------------------
-DefinitionDataFootprint *
-DeclStateReverter::createDefinitionDataFootprint(const ASTContext &Ctx,
- const CXXRecordDecl &RD) {
- auto *FP =
- new (Ctx, alignof(DefinitionDataFootprint)) DefinitionDataFootprint();
+DefinitionDataFootprint
+DeclStateReverter::createDefinitionDataFootprint(const CXXRecordDecl &RD) {
+ DefinitionDataFootprint FP;
const auto &Live = RD.data();
-#define FIELD(Name, Width, Merge) FP->Name = Live.Name;
+#define FIELD(Name, Width, Merge) FP.Name = Live.Name;
#include "clang/AST/CXXRecordDeclDefinitionBits.def"
return FP;
}
@@ -235,39 +233,16 @@ NamedDecl *DeclStateReverter::tryDetachRedeclChain(Decl *D, PTUID ID) {
Survivor = SurvivorDecl;
};
- switch (D->getKind()) {
- case Decl::Function:
- unlinkRedeclChain(cast<FunctionDecl>(D));
- break;
-
- case Decl::Var:
- unlinkRedeclChain(cast<VarDecl>(D));
- break;
-
- case Decl::Enum:
- case Decl::Record:
- case Decl::CXXRecord:
- unlinkRedeclChain(cast<TagDecl>(D));
- break;
-
- case Decl::ClassTemplate: {
- // NamedDecl *ND = cast<RedeclarableTemplateDecl>(D)->getTemplatedDecl();
- unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
- // tryDetachRedeclChain(ND);
- } break;
-
- case Decl::FunctionTemplate:
- case Decl::TypeAliasTemplate:
- case Decl::VarTemplate:
- unlinkRedeclChain(cast<RedeclarableTemplateDecl>(D));
- break;
- case Decl::Namespace:
- unlinkRedeclChain(cast<NamespaceDecl>(D));
- break;
-
- default:
- break;
- }
+ if (auto *FD = dyn_cast<FunctionDecl>(D))
+ unlinkRedeclChain(FD);
+ else if (auto *VD = dyn_cast<VarDecl>(D))
+ unlinkRedeclChain(VD);
+ else if (auto *TD = dyn_cast<TagDecl>(D))
+ unlinkRedeclChain(TD);
+ else if (auto *RT = dyn_cast<RedeclarableTemplateDecl>(D))
+ unlinkRedeclChain(RT);
+ else if (auto *NS = dyn_cast<NamespaceDecl>(D))
+ unlinkRedeclChain(NS);
return Survivor;
}
@@ -492,25 +467,28 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
Verified |= FlaggedKinds & uint32_t(MutationType::DefinitionData);
if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
// const Type *Now = DeclStateReverter::getRawTypeForDecl(RD);
- // if (Tracker.getTagDeclTypeInfo().mostRecent(RD).value_or(nullptr) !=
- // Now)
+ // const auto *Last =
+ // Tracker.Footprints.mostRecent<const Type *>(RD,
+ // MutationType::TypeForDecl);
+ // assert(Last && "TypeForDecl baseline missing at verify time");
+ // if (!Last || *Last != Now)
// Verified |= uint32_t(MutationType::TypeForDecl);
}
if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
- auto *Chain = Tracker.getChainFor(Spec);
- assert(Chain && "SpecInfo baseline missing at verify time");
- const SpecializationFootprint *Last =
- Chain ? Chain->mostRecent() : nullptr;
+ const auto *Last =
+ Tracker.Footprints.mostRecent<SpecializationFootprint>(
+ Spec, MutationType::SpecInfo);
+ assert(Last && "SpecInfo baseline missing at verify time");
if (!Last ||
!DeclStateReverter::compareSpecializationFootprint(*Last, *Spec))
Verified |= uint32_t(MutationType::SpecInfo);
}
} else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
- auto *Chain = Tracker.getMemberSpecChainFor(RD);
- assert(Chain && "MemberSpecInfo baseline missing at verify time");
- const MemberSpecializationFootprint *Last =
- Chain ? Chain->mostRecent() : nullptr;
+ const auto *Last =
+ Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+ RD, MutationType::MemberSpecInfo);
+ assert(Last && "MemberSpecInfo baseline missing at verify time");
if (!Last ||
!DeclStateReverter::compareMemberSpecializationFootprint(*Last, *RD))
Verified |= uint32_t(MutationType::MemberSpecInfo);
@@ -520,18 +498,18 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
case DeclShape::Function: {
const auto *FD = cast<FunctionDecl>(D);
if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
- auto *Chain = Tracker.getChainFor(FD);
- assert(Chain && "SpecInfo baseline missing at verify time");
- const FunctionSpecializationFootprint *Last =
- Chain ? Chain->mostRecent() : nullptr;
+ const auto *Last =
+ Tracker.Footprints.mostRecent<FunctionSpecializationFootprint>(
+ FD, MutationType::SpecInfo);
+ assert(Last && "SpecInfo baseline missing at verify time");
if (!Last || !DeclStateReverter::compareFunctionSpecializationFootprint(
*Last, *FD))
Verified |= uint32_t(MutationType::SpecInfo);
} else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
- auto *Chain = Tracker.getMemberSpecChainFor(FD);
- assert(Chain && "MemberSpecInfo baseline missing at verify time");
- const MemberSpecializationFootprint *Last =
- Chain ? Chain->mostRecent() : nullptr;
+ const auto *Last =
+ Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+ FD, MutationType::MemberSpecInfo);
+ assert(Last && "MemberSpecInfo baseline missing at verify time");
if (!Last ||
!DeclStateReverter::compareMemberSpecializationFootprint(*Last, *FD))
Verified |= uint32_t(MutationType::MemberSpecInfo);
@@ -546,19 +524,19 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
const auto *VD = cast<VarDecl>(D);
if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
- auto *Chain = Tracker.getChainFor(Spec);
- assert(Chain && "SpecInfo baseline missing at verify time");
- const VarSpecializationFootprint *Last =
- Chain ? Chain->mostRecent() : nullptr;
+ const auto *Last =
+ Tracker.Footprints.mostRecent<VarSpecializationFootprint>(
+ Spec, MutationType::SpecInfo);
+ assert(Last && "SpecInfo baseline missing at verify time");
if (!Last ||
!DeclStateReverter::compareVarSpecializationFootprint(*Last, *Spec))
Verified |= uint32_t(MutationType::SpecInfo);
}
} else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
- auto *Chain = Tracker.getMemberSpecChainFor(VD);
- assert(Chain && "MemberSpecInfo baseline missing at verify time");
- const MemberSpecializationFootprint *Last =
- Chain ? Chain->mostRecent() : nullptr;
+ const auto *Last =
+ Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+ VD, MutationType::MemberSpecInfo);
+ assert(Last && "MemberSpecInfo baseline missing at verify time");
if (!Last ||
!DeclStateReverter::compareMemberSpecializationFootprint(*Last, *VD))
Verified |= uint32_t(MutationType::MemberSpecInfo);
@@ -578,15 +556,18 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
const auto *ED = cast<EnumDecl>(D);
// if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
// const Type *Now = DeclStateReverter::getRawTypeForDecl(ED);
- // if (Tracker.getTagDeclTypeInfo().mostRecent(ED).value_or(nullptr) !=
- // Now)
+ // const auto *Last =
+ // Tracker.Footprints.mostRecent<const Type *>(ED,
+ // MutationType::TypeForDecl);
+ // assert(Last && "TypeForDecl baseline missing at verify time");
+ // if (!Last || *Last != Now)
// Verified |= uint32_t(MutationType::TypeForDecl);
// }
if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
- auto *Chain = Tracker.getMemberSpecChainFor(ED);
- assert(Chain && "MemberSpecInfo baseline missing at verify time");
- const MemberSpecializationFootprint *Last =
- Chain ? Chain->mostRecent() : nullptr;
+ const auto *Last =
+ Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+ ED, MutationType::MemberSpecInfo);
+ assert(Last && "MemberSpecInfo baseline missing at verify time");
if (!Last ||
!DeclStateReverter::compareMemberSpecializationFootprint(*Last, *ED))
Verified |= uint32_t(MutationType::MemberSpecInfo);
@@ -802,43 +783,38 @@ void PTUMutationActions::commitMembers(PTUID ID, const DeclContext *Members) {
void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
MutationRecord &Rec, bool IsNew) {
- // TypeForDecl is shared across the whole redeclaration chain – the Type
- // object itself is owned by whichever redecl first materialized it, so
- // every redecl’s cached pointer needs to be checked, not just RD’s.
- if (Rec.has(MutationType::TypeForDecl)) {
+ using ClassMutation = MutationType;
+
+ if (Rec.has(ClassMutation::TypeForDecl)) {
// for (const TagDecl *Redecl : RD->redecls()) {
- // const Type *LastKnown =
- // Tracker.TagdeclInfos.mostRecent(Redecl).value_or(nullptr);
- // TODO: need to handle?
+ // const auto *Last = Tracker.getFootprints().mostRecent<const Type *>(
+ // Redecl, MutationType::TypeForDecl);
+ // const Type *LastKnown = Last ? *Last : nullptr;
+ // if (LastKnown != cast<TypeDecl>(Redecl)->getTypeForDecl())
+ // Tracker.commitFootprint(Redecl, MutationType::TypeForDecl, ID,
+ // LastKnown);
// }
}
- // DefinitionArrived and DefinitionDataChanged are committed the same way –
- // both mean “this record’s DefinitionData needs a fresh footprint.” They
- // remain separate kinds because rollback and dependency-edge attribution
- // treat them differently: an arrival means this PTU completed a record
- // that predates it, while a data change may be either.
- if (Rec.has(MutationType::DefinitionInstantiate) ||
- Rec.has(MutationType::DefinitionData)) {
- Tracker.chainFor(RD).commit(
- ID, DeclStateReverter::createDefinitionDataFootprint(Tracker.Ctx, *RD));
+ if (Rec.has(ClassMutation::DefinitionInstantiate) ||
+ Rec.has(ClassMutation::DefinitionData)) {
+ Tracker.commitFootprint(
+ RD, MutationType::DefinitionData, ID,
+ DeclStateReverter::createDefinitionDataFootprint(*RD));
/// SpecializationDecl can have lazy implicit generated body;
- if (Rec.has(MutationType::DefinitionInstantiate)) {
+ if (Rec.has(ClassMutation::DefinitionInstantiate)) {
if (RD->getMemberSpecializationInfo())
commitMembers(ID, cast<DeclContext>(RD));
}
}
- if (Rec.has(MutationType::SpecInfo)) {
- // Only a specialization can carry this kind; a plain CXXRecordDecl
- // reaching here means a note*() wrapper passed the wrong kind.
+ if (Rec.has(ClassMutation::SpecInfo)) {
const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD);
- // assert(Spec && "SpecializationAdded noted on a non-specialization");
if (Spec) {
- Tracker.chainFor(Spec).commit(
- ID,
- DeclStateReverter::createSpecializationFootprint(Tracker.Ctx, *Spec));
+ Tracker.commitFootprint(
+ Spec, MutationType::SpecInfo, ID,
+ DeclStateReverter::createSpecializationFootprint(*Spec));
// Member declarations are instantiated eagerly with the class
// definition regardless of TSK; only their definitions defer. Walk
// them now so each member's MemberSpecializationInfo is tracked from
@@ -848,13 +824,168 @@ void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
}
}
- if (Rec.has(MutationType::MemberSpecInfo)) {
+ if (Rec.has(ClassMutation::MemberSpecInfo)) {
// A nested member of a class template -- its MSI's kind or
// point-of-instantiation changed.
if (RD->getMemberSpecializationInfo())
- Tracker.memberSpecChainFor(RD).commit(
- ID, DeclStateReverter::createMemberSpecializationFootprint(
- Tracker.Ctx, *RD));
+ Tracker.commitFootprint(
+ RD, MutationType::MemberSpecInfo, ID,
+ DeclStateReverter::createMemberSpecializationFootprint(*RD));
+ }
+}
+
+void PTUMutationActions::commitFunction(PTUID ID, const FunctionDecl *FD,
+ MutationRecord &Rec, bool IsNew) {
+
+ if (Rec.has(MutationType::ExceptionSpec) ||
+ Rec.has(MutationType::DeducedReturnType)) {
+ for (const FunctionDecl *Redecl : FD->redecls()) {
+ if (IsNew) {
+ Tracker.commitFootprint(Redecl, MutationType::ExceptionSpec, ID,
+ Redecl->getType());
+ } else {
+ const auto *Last = Tracker.Footprints.mostRecent<QualType>(
+ Redecl, MutationType::ExceptionSpec);
+ QualType LastKnown = Last ? *Last : QualType();
+ if (LastKnown != Redecl->getType())
+ Tracker.commitFootprint(Redecl, MutationType::ExceptionSpec, ID,
+ LastKnown);
+ }
+ }
+ }
+
+ // if (!IsNew && Rec.has(MutationType::DefinitionInstantiate)) {
+ // }
+
+ if (Rec.has(MutationType::SpecInfo)) {
+ assert(FD->getTemplateSpecializationKind() != TSK_Undeclared &&
+ "SpecInfoChanged on a function with no specialization info");
+ Tracker.commitFootprint(
+ FD, MutationType::SpecInfo, ID,
+ DeclStateReverter::createFunctionSpecializationFootprint(*FD));
+ }
+
+ if (Rec.has(MutationType::MemberSpecInfo)) {
+ if (FD->getMemberSpecializationInfo())
+ Tracker.commitFootprint(
+ FD, MutationType::MemberSpecInfo, ID,
+ DeclStateReverter::createMemberSpecializationFootprint(*FD));
+ }
+}
+
+void PTUMutationActions::commitVar(PTUID ID, const VarDecl *VD,
+ MutationRecord &Rec, bool IsNew) {
+
+ // EvaluatedStmt / APValue: populated on the FIRST constant-evaluation of
+ // this variable, which can land in any PTU long after the VarDecl was
+ // created. This is a mutation of an earlier PTU's decl, not a property
+ // settled at declaration time.
+ if (Rec.has(MutationType::EvaluatedValue)) {
+ // No footprint chain -- write-once field with a fixed null prior
+ // state (see restoreVar), so nothing to seed a baseline for.
+ if (IsNew) {
+ // Just created: register VD so a later commit's
+ // SweepTracker::sweep() call notices if/when WasEvaluated actually
+ // flips (no listener exists to tell us directly -- see
+ // hiddenKindsMask).
+ HiddenMutationTracker.track(VD, uint32_t(MutationType::EvaluatedValue));
+ } else {
+ // This IS the confirmation, arriving here via SweepTracker::sweep()'s
+ // OnConfirmed callback (there is no other path to this bit -- no
+ // listener exists for it). Write-once means terminal: once
+ // WasEvaluated is true it can never become false again on its own,
+ // so settle the bit now rather than waiting for the next sweep to
+ // notice kindsNeedingTracking no longer flags it. restoreVar
+ // is what re-track()s it, only if a rollback actually reverts this.
+ HiddenMutationTracker.settle(VD, uint32_t(MutationType::EvaluatedValue));
+ }
+ }
+
+ // if (!IsNew && Rec.has(MutationType::DefinitionInstantiate)) {
+ // }
+
+ if (Rec.has(MutationType::SpecInfo)) {
+ // Unlike functions, a variable specialization IS a distinct type.
+ const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
+ Tracker.commitFootprint(
+ Spec, MutationType::SpecInfo, ID,
+ DeclStateReverter::createVarSpecializationFootprint(*Spec));
+ }
+
+ if (Rec.has(MutationType::MemberSpecInfo)) {
+ if (VD->getMemberSpecializationInfo())
+ Tracker.commitFootprint(
+ VD, MutationType::MemberSpecInfo, ID,
+ DeclStateReverter::createMemberSpecializationFootprint(*VD));
+ }
+}
+
+void PTUMutationActions::commitEnum(PTUID ID, const EnumDecl *ED,
+ MutationRecord &Rec, bool IsNew) {
+
+ if (Rec.has(MutationType::TypeForDecl)) {
+ // for (const TagDecl *Redecl : ED->redecls()) {
+ // const Type *Now = Redecl->getTypeForDecl();
+ // const Type *LastKnown = nullptr;
+ // if (!IsNew) {
+ // const auto *Last = Tracker.getFootprints().mostRecent<const Type *>(
+ // Redecl, MutationType::TypeForDecl);
+ // LastKnown = Last ? *Last : nullptr;
+ // }
+ // if (IsNew || (LastKnown != Now))
+ // Tracker.commitFootprint(Redecl, MutationType::TypeForDecl, ID,
+ // LastKnown);
+ // }
+ }
+
+ if (Rec.has(MutationType::MemberSpecInfo)) {
+ // A scoped member enumeration of a class template -- instantiated with
+ // the enclosing specialization, carrying MSI back to the pattern enum.
+ if (ED->getMemberSpecializationInfo())
+ Tracker.commitFootprint(
+ ED, MutationType::MemberSpecInfo, ID,
+ DeclStateReverter::createMemberSpecializationFootprint(*ED));
+ }
+}
+
+void PTUMutationActions::commitTemplate(PTUID ID,
+ const RedeclarableTemplateDecl *RT,
+ MutationRecord &Rec, bool IsNew) {
+ // HiddenMutationTracker keeps both flags per declaration, and Cur.Mutations
+ // (Rec, per PTU) already tells us whether this PTU confirmed it, which is
+ // exactly what restoreTemplate needs, just like restoreVar. Handle both
+ // flags the same way: IsNew registers the declaration for a future sweep;
+ // otherwise, this is the confirmation from SweepTracker::sweep()'s
+ // OnConfirmed callback. Once either flag is true, it is final—
+ // Common/CanonInjectedTST won't reset it on their own—so settle it now
+ // instead of waiting for another sweep.
+ const RedeclarableTemplateDecl *RTCanon = RT->getCanonicalDecl();
+ if (Rec.has(MutationType::TemplateCommon)) {
+ if (IsNew)
+ HiddenMutationTracker.track(RTCanon,
+ uint32_t(MutationType::TemplateCommon));
+ else
+ HiddenMutationTracker.settle(RTCanon,
+ uint32_t(MutationType::TemplateCommon));
+ }
+
+ if (Rec.has(MutationType::CanonInjectedTST)) {
+ if (IsNew)
+ HiddenMutationTracker.track(RTCanon,
+ uint32_t(MutationType::CanonInjectedTST));
+ else
+ HiddenMutationTracker.settle(RTCanon,
+ uint32_t(MutationType::CanonInjectedTST));
+ }
+}
+
+void PTUMutationActions::commitTypedef(PTUID ID, const TypedefNameDecl *TD,
+ MutationRecord &Rec, bool IsNew) {
+ if (Rec.has(MutationType::TypeForDecl)) {
+ if (IsNew)
+ HiddenMutationTracker.track(TD, uint32_t(MutationType::TypeForDecl));
+ else
+ HiddenMutationTracker.settle(TD, uint32_t(MutationType::TypeForDecl));
}
}
@@ -872,6 +1003,33 @@ void PTUMutationActions::commitDecl(PTUID ID, const Decl *D,
}
}
+// respect the LIFO so only current inside map not commited can be commited
+// not randon PTUID
+// global map info shouldn't be commited before only added here. not note*
+// time.
+void PTUMutationActions::commit(TranslationUnitDecl *ThisTU) {
+ PTUStateInfo &Cur = Tracker.current();
+ assert(!Cur.Commited);
+ PTUID ID = Cur.ID;
+
+ Cur.verifyMutations(*this);
+ Tracker.getHiddenMutationTracker().sweep(
+ *this, [&](const Decl *D, DeclShape S, uint32_t K) {
+ Cur.noteMutated(D, S, K);
+ });
+
+ for (auto &[D, Rec] : Cur.Mutations)
+ commitDecl(ID, D, Rec, /*IsNew=*/false);
+
+ DeclStateCommitPolicy Proxy(ID, *this);
+ for (const Decl *D : Cur.ImplicitDecls)
+ Proxy.process(D);
+
+ walkDecls(ThisTU, Proxy);
+
+ Cur.Commited = true;
+}
+
/// -----------------------------------------------------------------------
//////////////////////// PTUMutationActions::restore //////////////////////
/// -----------------------------------------------------------------------
@@ -879,36 +1037,25 @@ void PTUMutationActions::commitDecl(PTUID ID, const Decl *D,
void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
MutationRecord &Rec) {
- // DefinitionArrived and DefinitionDataChanged commit identically -- both
- // mean "this record's DefinitionData needs a fresh footprint." They stay
- // distinct kinds because rollback and dependency-edge attribution treat
- // them differently (arrival is this PTU completing a record that
- // predates it; a data change may be either).
if (Rec.has(MutationType::DefinitionInstantiate) ||
Rec.has(MutationType::DefinitionData)) {
- // CXXRecordDecl *RD = cast<CXXRecordDecl>(RD);
- // New: no prior entry exists, so write unconditionally.
- // Existing: only write a real diff.
- /// SpecializationDecl can have lazy implicit generated body;
if (Rec.has(MutationType::DefinitionInstantiate)) {
DeclStateReverter::revertDefinitionArrival(
*const_cast<CXXRecordDecl *>(RD));
- } else {
- auto *Chain = Tracker.getChainFor(RD);
- if (Chain) {
- if (const DefinitionDataFootprint *Restored = Chain->mostRecent())
- DeclStateReverter::restoreDefinitionDataFootprint(
- *Restored, *const_cast<CXXRecordDecl *>(RD));
- }
+ } else if (const auto *Restored =
+ Tracker.getFootprints().mostRecent<DefinitionDataFootprint>(
+ RD, MutationType::DefinitionData)) {
+ DeclStateReverter::restoreDefinitionDataFootprint(
+ *Restored, *const_cast<CXXRecordDecl *>(RD));
}
}
// TypeForDecl is shared across the whole redeclaration chain -- revert all.
if (Rec.has(MutationType::TypeForDecl)) {
- // CXXRecordDecl *RD = cast<CXXRecordDecl>(D);
// for (const TagDecl *Redecl : RD->redecls()) {
- // const Type *LastKnown =
- // Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+ // const auto *Last = Tracker.Footprints.mostRecent<const Type *>(
+ // Redecl, MutationType::TypeForDecl);
+ // const Type *LastKnown = Last ? *Last : nullptr;
// TODO: need to handle?
// }
}
@@ -918,22 +1065,22 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
// reaching here means a note*() wrapper passed the wrong kind.
const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD);
assert(Spec && "SpecializationAdded noted on a non-specialization");
- if (auto *Chain = Tracker.getChainFor(Spec)) {
- if (const SpecializationFootprint *Restored = Chain->mostRecent())
- DeclStateReverter::restoreSpecializationFootprint(
- *Restored, *const_cast<ClassTemplateSpecializationDecl *>(Spec));
- }
+ if (const SpecializationFootprint *Restored =
+ Tracker.getFootprints().mostRecent<SpecializationFootprint>(
+ Spec, MutationType::SpecInfo))
+ DeclStateReverter::restoreSpecializationFootprint(
+ *Restored, *const_cast<ClassTemplateSpecializationDecl *>(Spec));
}
if (Rec.has(MutationType::MemberSpecInfo)) {
// A nested member of a class template – its MSI kind or
// point-of-instantiation changed.
assert(RD->getMemberSpecializationInfo());
- if (auto *Chain = Tracker.getMemberSpecChainFor(RD)) {
- if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
- DeclStateReverter::restoreMemberSpecializationFootprint(
- *Restored, *const_cast<CXXRecordDecl *>(RD));
- }
+ if (const MemberSpecializationFootprint *Restored =
+ Tracker.getFootprints().mostRecent<MemberSpecializationFootprint>(
+ RD, MutationType::MemberSpecInfo))
+ DeclStateReverter::restoreMemberSpecializationFootprint(
+ *Restored, *const_cast<CXXRecordDecl *>(RD));
}
}
@@ -947,9 +1094,9 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
if (Rec.has(MutationType::ExceptionSpec) ||
Rec.has(MutationType::DeducedReturnType)) {
for (const FunctionDecl *Redecl : FD->redecls()) {
- QualType LastKnown =
- Tracker.getFunctionTypeMutations().mostRecent(Redecl).value_or(
- QualType());
+ const auto *Last = Tracker.getFootprints().mostRecent<QualType>(
+ Redecl, MutationType::ExceptionSpec);
+ QualType LastKnown = Last ? *Last : QualType();
if (LastKnown != Redecl->getType())
const_cast<FunctionDecl *>(Redecl)->setType(LastKnown);
}
@@ -963,20 +1110,20 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
// FunctionTemplateSpecializationInfo.
assert(FD->getTemplateSpecializationKind() != TSK_Undeclared &&
"SpecInfoChanged on a function with no specialization info");
- if (auto *Chain = Tracker.getChainFor(FD)) {
- if (const FunctionSpecializationFootprint *Restored = Chain->mostRecent())
- DeclStateReverter::restoreFunctionSpecializationFootprint(
- *Restored, *const_cast<FunctionDecl *>(FD));
- }
+ if (const auto *Restored =
+ Tracker.Footprints.mostRecent<FunctionSpecializationFootprint>(
+ FD, MutationType::SpecInfo))
+ DeclStateReverter::restoreFunctionSpecializationFootprint(
+ *Restored, *const_cast<FunctionDecl *>(FD));
}
if (Rec.has(MutationType::MemberSpecInfo)) {
assert(FD->getMemberSpecializationInfo());
- if (auto *Chain = Tracker.getMemberSpecChainFor(FD)) {
- if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
- DeclStateReverter::restoreMemberSpecializationFootprint(
- *Restored, *const_cast<FunctionDecl *>(FD));
- }
+ if (const auto *Restored =
+ Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+ FD, MutationType::MemberSpecInfo))
+ DeclStateReverter::restoreMemberSpecializationFootprint(
+ *Restored, *const_cast<FunctionDecl *>(FD));
}
}
@@ -1045,27 +1192,26 @@ void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
}
if (Rec.has(VarMutation::DefinitionInstantiate)) {
- /// TODO;
DeclStateReverter::revertDefinitionArrival(*const_cast<VarDecl *>(VD));
}
if (Rec.has(VarMutation::SpecInfo)) {
// Unlike functions, a variable specialization IS a distinct type.
const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
- if (auto *Chain = Tracker.getChainFor(Spec)) {
- if (const VarSpecializationFootprint *Restored = Chain->mostRecent())
- DeclStateReverter::restoreVarSpecializationFootprint(
- *Restored, *const_cast<VarTemplateSpecializationDecl *>(Spec));
- }
+ if (const auto *Restored =
+ Tracker.getFootprints().mostRecent<VarSpecializationFootprint>(
+ Spec, MutationType::SpecInfo))
+ DeclStateReverter::restoreVarSpecializationFootprint(
+ *Restored, *const_cast<VarTemplateSpecializationDecl *>(Spec));
}
if (Rec.has(VarMutation::MemberSpecInfo)) {
assert(VD->getMemberSpecializationInfo());
- if (auto *Chain = Tracker.getMemberSpecChainFor(VD)) {
- if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
- DeclStateReverter::restoreMemberSpecializationFootprint(
- *Restored, *const_cast<VarDecl *>(VD));
- }
+ if (const auto *Restored =
+ Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+ VD, MutationType::MemberSpecInfo))
+ DeclStateReverter::restoreMemberSpecializationFootprint(
+ *Restored, *const_cast<VarDecl *>(VD));
}
}
@@ -1077,24 +1223,26 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
// defined later), so every redecl's cached pointer needs checking.
if (Rec.has(MutationType::TypeForDecl)) {
// for (const TagDecl *Redecl : ED->redecls()) {
- // const Type *LastKnown =
- // Tracker.getTagDeclTypeInfo().mostRecent(Redecl).value_or(nullptr);
+ // const auto *Last = Tracker.Footprints.mostRecent<const Type *>(
+ // Redecl, MutationType::TypeForDecl);
+ // const Type *LastKnown = Last ? *Last : nullptr;
// TODO: need to handle?
// }
}
- if (Rec.has(MutationType::DefinitionInstantiate))
- DeclStateReverter::revertDefinitionArrival(*const_cast<EnumDecl *>(ED));
+ // if (Rec.has(MutationType::DefinitionInstantiate))
+ // DeclStateReverter::revertDefinitionArrival(*const_cast<EnumDecl
+ // *>(ED));
if (Rec.has(MutationType::MemberSpecInfo)) {
// A scoped member enumeration of a class template -- instantiated with
// the enclosing specialization, carrying MSI back to the pattern enum.
assert(ED->getMemberSpecializationInfo());
- if (auto *Chain = Tracker.getMemberSpecChainFor(ED)) {
- if (const MemberSpecializationFootprint *Restored = Chain->mostRecent())
- DeclStateReverter::restoreMemberSpecializationFootprint(
- *Restored, *const_cast<EnumDecl *>(ED));
- }
+ if (const auto *Restored =
+ Tracker.getFootprints().mostRecent<MemberSpecializationFootprint>(
+ ED, MutationType::MemberSpecInfo))
+ DeclStateReverter::restoreMemberSpecializationFootprint(
+ *Restored, *const_cast<EnumDecl *>(ED));
}
}
@@ -1340,4 +1488,17 @@ void PTUMutationRecorder::InstantiationRequested(const ValueDecl *D) {
Cur.noteMutated(D, S, K);
}
+// Each (Owner, Kind) key written by this PTU has its trailing entries
+// removed in one uniform call. There’s no need for per-node-type
+// release() or pool bookkeeping: FootprintStore’s SmallVector-per-key
+// storage manages the memory normally. Once a key’s history is empty,
+// removeFrom erases the key entirely, just as it always has.
+void IncrementalStateTracker::undoLastEntries() {
+ PTUStateInfo &Cur = current();
+ PTUID ID = Cur.ID;
+
+ for (auto &[Owner, K] : Cur.TouchedFootprints)
+ Footprints.removeFrom(Owner, K, ID);
+}
+
} // end namespace clang
\ No newline at end of file
>From ff21c1a2ea61f9d80bdea9e0f25763ec31bc2bae Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Sun, 27 Sep 2026 17:45:55 +0530
Subject: [PATCH 10/13] Improve: add logic that verify unconfirmed mutations
from listeners and refactor other part of code
---
.../include/clang/Interpreter/ErrorRecovery.h | 183 ++----
clang/lib/Interpreter/ErrRecovery.cpp | 615 +++++++++---------
clang/lib/Interpreter/IncrementalParser.cpp | 2 -
3 files changed, 369 insertions(+), 431 deletions(-)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 2c662e9e53017..b8365efc41968 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -17,6 +17,7 @@
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
+#include "llvm/ADT/BitVector.h"
#include <variant>
namespace clang {
@@ -265,96 +266,24 @@ class DeclStateReverter {
}
protected:
- // These all return by value now, not an ASTContext-arena-allocated
- // pointer: footprints live inside FootprintStore's own SmallVector, which
- // owns them with ordinary C++ lifetime (ctor/dtor/copy), so there's
- // nothing left for arena allocation to buy -- the arena's job was only
- // ever to make these survive without anyone having to free them, and a
- // normally-owned value does that for free too.
static DefinitionDataFootprint
createDefinitionDataFootprint(const CXXRecordDecl &RD);
- static bool compareDefinitionDataFootprint(const DefinitionDataFootprint &FP,
- const CXXRecordDecl &RD);
-
static void restoreDefinitionDataFootprint(const DefinitionDataFootprint &FP,
CXXRecordDecl &RD);
- static SpecializationFootprint
- createSpecializationFootprint(const ClassTemplateSpecializationDecl &Spec) {
- SpecializationFootprint FP;
- FP.update(Spec);
- return FP;
- }
- static bool
- compareSpecializationFootprint(const SpecializationFootprint &FP,
- const ClassTemplateSpecializationDecl &Spec) {
- SpecializationFootprint Live;
- Live.update(Spec);
- return FP == Live;
- }
- static void
- restoreSpecializationFootprint(const SpecializationFootprint &FP,
- ClassTemplateSpecializationDecl &Spec) {
- FP.restore(Spec);
- }
-
- static VarSpecializationFootprint
- createVarSpecializationFootprint(const VarTemplateSpecializationDecl &Spec) {
- VarSpecializationFootprint FP;
- FP.update(Spec);
- return FP;
- }
- static bool
- compareVarSpecializationFootprint(const VarSpecializationFootprint &FP,
- const VarTemplateSpecializationDecl &Spec) {
- VarSpecializationFootprint Live;
- Live.update(Spec);
- return FP == Live;
- }
- static void
- restoreVarSpecializationFootprint(const VarSpecializationFootprint &FP,
- VarTemplateSpecializationDecl &Spec) {
- FP.restore(Spec);
- }
-
- static FunctionSpecializationFootprint
- createFunctionSpecializationFootprint(const FunctionDecl &FD) {
- FunctionSpecializationFootprint FP;
- FP.update(FD);
- return FP;
- }
- static bool compareFunctionSpecializationFootprint(
- const FunctionSpecializationFootprint &FP, const FunctionDecl &FD) {
- FunctionSpecializationFootprint Live;
- Live.update(FD);
- return FP == Live;
- }
- static void restoreFunctionSpecializationFootprint(
- const FunctionSpecializationFootprint &FP, FunctionDecl &FD) {
- FP.restore(FD);
- }
-
- template <typename OwnerT>
- static MemberSpecializationFootprint
- createMemberSpecializationFootprint(const OwnerT &D) {
- MemberSpecializationFootprint FP;
- FP.update(D);
+ // Generic across every other footprint type -- SpecializationFootprint,
+ // VarSpecializationFootprint, FunctionSpecializationFootprint,
+ // MemberSpecializationFootprint.
+ template <typename FootprintT, typename OwnerT>
+ static FootprintT createFootprint(const OwnerT &Owner) {
+ FootprintT FP;
+ FP.update(Owner);
return FP;
}
- template <typename OwnerT>
- static bool
- compareMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
- const OwnerT &D) {
- MemberSpecializationFootprint Live;
- Live.update(D);
- return FP == Live;
- }
- template <typename OwnerT>
- static void
- restoreMemberSpecializationFootprint(const MemberSpecializationFootprint &FP,
- OwnerT &D) {
- FP.restore(D);
+ template <typename FootprintT, typename OwnerT>
+ static void restoreFootprint(const FootprintT &FP, OwnerT &Owner) {
+ FP.restore(Owner);
}
static void restoreDefinitionAndRevertDC(CXXRecordDecl &RD);
@@ -389,10 +318,6 @@ class DeclStateReverter {
// specializations added by a given PTU form a trailing run at the back of
// the FoldingSetVector. Popping entries while the back entry belongs to
// this PTU is therefore sufficient.
- //
- // FoldingSetVector also has no arbitrary-position erase – only
- // pop_back()/clear() – which is another reason to walk from the back
- // rather than filter the entries in place.
static void removeSpecializations(PTUCheckpointLedger &Ledger,
const RedeclarableTemplateDecl *TD,
PTUID ID);
@@ -429,13 +354,12 @@ class DeclStateReverter {
static void detachCommonBase(const RedeclarableTemplateDecl *RT);
- /// Remove D from its semantic context's lookup map, reinstating the
- /// previous declaration if D had replaced one in-place (which is what
- /// StoredDeclsList::HandleRedeclaration does on a redeclaration --
- /// erasing the slot outright would lose the older decl entirely; that
- /// is the ReopenNs failure).
void detachFromDCLookup(Decl *D, PTUID ID);
+ void detachFromExternCLookup(Decl *D, DeclContext *ExternCCtx, PTUID ID);
+
+ static void removeFromIdResolver(Sema &S, NamedDecl *D);
+
// Walk D's redecl chain looking for the newest decl that predates this
// PTU. Returns nullptr if the entire chain was created this PTU.
template <typename DeclT> DeclT *findSurvivor(DeclT *D, PTUID ID) const;
@@ -453,18 +377,16 @@ class DeclStateReverter {
private:
NamedDecl *tryDetachRedeclChain(Decl *D, PTUID ID);
+
+ void removeFromLookupMap(NamedDecl *ND, DeclContext *DC, PTUID ID);
};
/// Maps addresses to the PTU that allocated them, by keeping the slab
/// checkpoint taken before each PTU began.
-///
-/// PTU IDs are the vector index, which is what makes the lookup a binary
-/// search: checkpoints are monotonically increasing, so "is this address
-/// after checkpoint N" is true for all N up to the allocating PTU and
-/// false after.
class PTUCheckpointLedger {
const ASTContext &Ctx;
llvm::SmallVector<llvm::SlabCheckPoint, 16> CheckpointBeforePTU;
+ llvm::BitVector Dead;
bool isAfter(const void *Ptr, PTUID ID) const {
return Ctx.getAllocator().isAfterCheckpoint(Ptr, CheckpointBeforePTU[ID]);
@@ -478,11 +400,14 @@ class PTUCheckpointLedger {
assert(ID == CheckpointBeforePTU.size() &&
"PTUs must be recorded in order");
CheckpointBeforePTU.push_back(CP);
+ Dead.push_back(false);
}
/// Was \p Ptr allocated during PTU \p ID or later?
bool isFromThisPTU(const void *Ptr, PTUID ID) const {
assert(ID < CheckpointBeforePTU.size());
+ assert(!Dead[ID] &&
+ "querying provenance against a PTU that no longer exists");
return isAfter(Ptr, ID);
}
@@ -500,13 +425,16 @@ class PTUCheckpointLedger {
Hi = Mid;
}
}
+ assert((!Result || !Dead[*Result]) &&
+ "Ptr belongs to a PTU that was already rolled back");
return Result;
}
- /// Drop checkpoints from \p ID onward, after rolling those PTUs back.
- void undoFrom(PTUID ID) {
- if (ID < CheckpointBeforePTU.size())
- CheckpointBeforePTU.resize(ID);
+ /// FIXME: Pop back CheckpointBeforePTU once memory restoration is supported.
+ /// For now, there is no memory restoration, so mark it dead.
+ void markDead(PTUID ID) {
+ assert(ID < Dead.size());
+ Dead[ID] = true;
}
};
@@ -569,8 +497,7 @@ class FootprintStore {
Store;
public:
- // Same dedup StateAwareChain::commit() always had: re-committing the
- // same value for the same owner+kind is a no-op, not a new history entry.
+ // commit decl state owner+mutation_kind -> Footprint.
template <typename T>
void commit(const Decl *Owner, MutationType K, PTUID ID, const T &Fresh) {
auto &History = Store[{Owner, K}];
@@ -580,9 +507,7 @@ class FootprintStore {
}
// nullptr means either "no history at all" or "history exists but the
- // most recent entry isn't a T" -- the latter never legitimately happens
- // for a given (Owner, K) pair, since K determines which alternative was
- // ever committed there.
+ // most recent entry isn't a T".
template <typename T>
const T *mostRecent(const Decl *Owner, MutationType K) const {
auto It = Store.find({Owner, K});
@@ -613,15 +538,21 @@ struct PTUStateInfo {
llvm::SmallPtrSet<const Decl *, 4> ImplicitDecls;
+ /// Set exactly when AddedCXXImplicitMember fires this PTU -- narrower
+ /// than ImplicitDecls above, which is a shared superset populated by
+ /// many unrelated listener paths too (template instantiation, etc.).
+ /// This is the one listener whose firing can leave a stale
+ /// Sema::SpecialMemberCache entry behind, so it's what
+ /// purgeStaleSpecialMemberCache actually needs to gate on -- gating on
+ /// ImplicitDecls instead would trigger that scan on almost every PTU,
+ /// defeating the point of the gate.
+ bool HadImplicitCXXMember = false;
+
/// here touched info mean other this belongs to other PTUs;
llvm::SmallPtrSet<const DeclContext *, 4> TouchedDC;
/// Every (Owner, Kind) key this PTU's own commit() wrote a FootprintStore
- /// entry for -- replaces the old TouchedFootprintDecls/
- /// TouchedFunctionTypeOwners/TouchedTagdeclOwners trio (one set per
- /// container that used to exist) with one list, since there's now only
- /// one container. Duplicates are harmless: FootprintStore::removeFrom is
- /// idempotent.
+ /// entry for.
llvm::SmallVector<std::pair<const Decl *, MutationType>, 8> TouchedFootprints;
explicit PTUStateInfo(PTUID ID) : ID(ID) {}
@@ -738,8 +669,7 @@ class IncrementalStateTracker {
// Writes a new value and records that this PTU touched (Owner, K), so
// undoLastEntries() knows what to remove if the PTU is rolled back
// without committing. mostRecent() doesn't need any bookkeeping, so it
- // reads directly from Footprints via Tracker.Footprints.mostRecent<T>(...)
- // instead of having its own wrapper.
+ // reads directly from Footprints via Tracker.Footprints.mostRecent<T>(...).
template <typename T>
void commitFootprint(const Decl *Owner, MutationType K, PTUID ID,
const T &Fresh) {
@@ -773,6 +703,7 @@ class IncrementalStateTracker {
SweepTracker &getHiddenMutationTracker() { return HiddenMutationTracker; }
PTUCheckpointLedger &getPTUSlabCheckpoints() { return PTUSlabCheckpoints; }
Sema &getSema() const { return SemaRef; }
+ ASTContext &getASTContext() const { return Ctx; }
void undoLastEntries();
@@ -780,8 +711,7 @@ class IncrementalStateTracker {
void popCurrentPTU() {
PTUID ID = currentID();
PTUStack.pop_back();
- PTUSlabCheckpoints.undoFrom(ID);
- NextID = ID;
+ PTUSlabCheckpoints.markDead(ID);
}
};
@@ -802,7 +732,11 @@ class PTUMutationActions {
uint32_t DeclNeedingTracking(DeclShape S, const Decl *D);
- uint32_t verifyMutationFor(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
+ uint32_t confirmMutation(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
+
+ void registerLiveVerification(const Decl *D, uint32_t Kinds);
+
+ void settleIfClosed(const Decl *D, DeclShape S, uint32_t Confirmed);
private:
void commitMembers(PTUID ID, const DeclContext *Members);
@@ -837,12 +771,23 @@ class PTUMutationActions {
void restoreDecl(PTUID ID, const Decl *D, MutationRecord &Rec);
void commitDecl(PTUID ID, const Decl *D, MutationRecord &Rec,
bool IsNew = false);
- // respect the LIFO so only current inside map not commited can be commited
- // not randon PTUID
- // global map info shouldn't be commited before only added here. not note*
- // time.
+ // Actual commit action: updates IncrementalStateTracker tracking info for
+ // decls from the previous PTU after a successful PTU. And add tracking info
+ // for newly created Decls
+ //
void commit(TranslationUnitDecl *MostRecentTU);
+
+ // Restore action: restores decls from the previous PTU using
+ // IncrementalStateTracker tracking info and unlinks decls created by this
+ // PTU.
void restore(TranslationUnitDecl *MostRecentTU);
+
+private:
+ // Sema::SpecialMemberCache, per (RD,
+ // kind+qualifiers), the CXXMethodDecl* a prior LookupSpecialMember()
+ // call resolved to, with no re-validation on a cache hit. remove every
+ // entry whose cached method belongs to the PTU being rolled back.
+ void restoreSpecialMemberCache(PTUID ID);
};
class PTUMutationRecorder : public ASTMutationListener {
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index 5c61e0165507f..509d0d0a40668 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -21,13 +21,13 @@
namespace clang {
-#define PTU_SHAPES \
- PTU_SHAPE(Class, CXXRecordDecl) \
- PTU_SHAPE(Function, FunctionDecl) \
- PTU_SHAPE(Var, VarDecl) \
- PTU_SHAPE(Enum, EnumDecl) \
- PTU_SHAPE(Template, RedeclarableTemplateDecl) \
- PTU_SHAPE(Typedef, TypedefNameDecl)
+#define DECL_SHAPES \
+ DECL_SHAPE(Class, CXXRecordDecl) \
+ DECL_SHAPE(Function, FunctionDecl) \
+ DECL_SHAPE(Var, VarDecl) \
+ DECL_SHAPE(Enum, EnumDecl) \
+ DECL_SHAPE(Template, RedeclarableTemplateDecl) \
+ DECL_SHAPE(Typedef, TypedefNameDecl)
/// -----------------------------------------------------------------------
//////////////////////// PTUMutationActions::Helpers ///////////////////////
@@ -42,15 +42,15 @@ DeclStateReverter::createDefinitionDataFootprint(const CXXRecordDecl &RD) {
return FP;
}
-bool DeclStateReverter::compareDefinitionDataFootprint(
- const DefinitionDataFootprint &FP, const CXXRecordDecl &RD) {
- const auto &Live = RD.data();
-#define FIELD(Name, Width, Merge) \
- if (FP.Name != Live.Name) \
- return false;
-#include "clang/AST/CXXRecordDeclDefinitionBits.def"
- return true;
-}
+// bool DeclStateReverter::compareDefinitionDataFootprint(
+// const DefinitionDataFootprint &FP, const CXXRecordDecl &RD) {
+// const auto &Live = RD.data();
+// #define FIELD(Name, Width, Merge) \
+// if (FP.Name != Live.Name) \
+// return false;
+// #include "clang/AST/CXXRecordDeclDefinitionBits.def"
+// return true;
+// }
void DeclStateReverter::restoreDefinitionDataFootprint(
const DefinitionDataFootprint &FP, CXXRecordDecl &RD) {
@@ -59,23 +59,15 @@ void DeclStateReverter::restoreDefinitionDataFootprint(
#include "clang/AST/CXXRecordDeclDefinitionBits.def"
}
-// NEEDS: friend class DeclStateReverter; in CXXRecordDecl (DefinitionData
-// is PRIVATE there -- already granted) and nothing further for the
-// DeclContext/TagDecl parts below, which are routed through the
-// protected accessors above instead.
void DeclStateReverter::restoreDefinitionAndRevertDC(CXXRecordDecl &RD) {
// 1. DefinitionData pointer -- back to null.
RD.DefinitionData = nullptr;
- // 2. DeclContext member list -- back to empty. RD's own FirstDecl/
- // LastDecl were populated by InstantiateClassImpl substituting RD's
- // members for the first time (SemaTemplateInstantiate.cpp:3658-3729,
- // and for the static_assert/friend-decl case specifically,
- // Sema::BuildStaticAssertDeclaration's CurContext->addDecl(), which
- // never notifies since neither is a NamedDecl) -- an incomplete,
- // never-defined class's DeclContext has neither, so this is RD's
- // true prior state, not a guess.
+ // 2. DeclContext member list -- back to empty.
clearDeclContextChain(RD);
+ DeclContext *Primary = RD.getDeclContext()->getPrimaryContext();
+ if (StoredDeclsMap *Map = Primary->getLookupPtr())
+ Map->clear();
// 3. TagDecl completion bits -- back to "never started". Flipped by
// startDefinition()/completeDefinition().
@@ -128,7 +120,6 @@ void DeclStateReverter::removeSpecializations(
}
void DeclStateReverter::detachDefData(const Decl *D) {
- // TODO: handle Template's own separate redecl chain the same way.
const auto *RD = dyn_cast<CXXRecordDecl>(D);
if (!RD)
return;
@@ -138,15 +129,35 @@ void DeclStateReverter::detachDefData(const Decl *D) {
}
void DeclStateReverter::detachCommonBase(const RedeclarableTemplateDecl *RT) {
- // Common is shared across the WHOLE redecl chain once path-compression
- // (RedeclarableTemplateDecl::getCommonPtr()'s backward walk-then-
- // backfill) has run -- resetting only the one decl this PTU created
- // would leave some other redecl holding a stale, dangling copy of the
- // same pointer. Walk every redecl, not just a backward chain from RT.
for (const auto *R : RT->redecls())
clearCommonPtr(*R);
}
+void DeclStateReverter::removeFromLookupMap(NamedDecl *ND, DeclContext *DC,
+ PTUID ID) {}
+
+void DeclStateReverter::detachFromExternCLookup(Decl *D,
+ DeclContext *ExternCCtx,
+ PTUID ID) {
+ auto *ND = dyn_cast<NamedDecl>(D);
+ if (!ND)
+ return;
+
+ bool IsExternC = false;
+ if (const auto *FD = dyn_cast<FunctionDecl>(ND))
+ IsExternC = FD->isExternC();
+ else if (const auto *VD = dyn_cast<VarDecl>(ND))
+ IsExternC = VD->isExternC();
+
+ if (IsExternC)
+ removeFromLookupMap(ND, ExternCCtx, ID);
+}
+
+void DeclStateReverter::removeFromIdResolver(Sema &S, NamedDecl *D) {
+ if (D->getDeclName().getFETokenInfo())
+ S.IdResolver.RemoveDecl(D);
+}
+
/// Remove D from every lookup map it became visible in, reinstating the
/// previous declaration where D had replaced one in-place (which is what
/// StoredDeclsList::HandleRedeclaration does on a redeclaration --
@@ -422,75 +433,48 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
return 0;
}
-/// Given a Decl known to be DeclShape S with FlaggedKinds set, return the
-/// subset of kinds that are actually confirmed.
-///
-/// Everything reaching this function through verifyMutations() belongs to
-/// a decl from an earlier PTU. Same-PTU mutations are ignored by the
-/// reactive listeners, so these mutations were already found during an
-/// earlier commit().
-///
-/// Some mutation kinds are fully confirmed by their listener, so there is
-/// no need to walk the chain for them:
-/// - DefinitionInstantiate, SpecializationAdded, DefinitionData
-/// - ExceptionSpec, DeducedReturnType
-///
-/// For these, the listener itself is the source of truth for the change.
-/// In particular, don’t use the FunctionTypeMutations chain for
-/// ExceptionSpec/DeducedReturnType here. That chain is populated later by
-/// commitFunctionFamily(), so checking it here would miss the first
-/// resolution.
+template <typename T>
+static bool differsFromLast(IncrementalStateTracker &Tracker, const Decl *Owner,
+ MutationType K, const T &LiveValue) {
+ const auto *Last = Tracker.getFootprints().mostRecent<T>(Owner, K);
+ assert(Last && "baseline missing at verify time");
+ return !Last || *Last != LiveValue;
+}
+
+template <typename FootprintT, typename OwnerT>
+static FootprintT Footprint(const OwnerT &Owner) {
+ FootprintT FP;
+ FP.update(Owner);
+ return FP;
+}
+
+/// The single place to check whether a kind actually changed.
///
-/// The remaining kinds don’t have a reliable listener, so they need to be
-/// checked against the stored state:
-/// - SpecInfo, MemberSpecInfo
-/// - Template Common/CanonInjectedTST
-/// - Var EvaluatedValue
-/// - Typedef TypeForDecl
+/// It handles both listener-less kinds and SpecInfo/MemberSpecInfo. The
+/// latter can be checked from both the sweep and the listener re-check.
///
-/// For footprint-based checks, the baseline should already be there since
-/// it is seeded when the decl is first discovered. If it’s missing here,
-/// assert in debug builds, but still confirm the mutation. commitDecl()
-/// will create the missing chain while committing, which fixes up the
-/// baseline for future checks.
-uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
- uint32_t FlaggedKinds) {
-
+/// Keeping everything in one function makes sure SpecInfo/MemberSpecInfo
+/// changes are caught even when the listener never fires.
+uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
+ uint32_t FlaggedKinds) {
uint32_t Verified = 0;
- Verified |= FlaggedKinds & uint32_t(MutationType::DefinitionInstantiate);
switch (S) {
case DeclShape::Class: {
const auto *RD = cast<CXXRecordDecl>(D);
- // AddedCXXImplicitMember firing is itself the confirmation -- see
- // this function's own top comment for why no chain lookup belongs
- // here.
- Verified |= FlaggedKinds & uint32_t(MutationType::DefinitionData);
if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
- // const Type *Now = DeclStateReverter::getRawTypeForDecl(RD);
- // const auto *Last =
- // Tracker.Footprints.mostRecent<const Type *>(RD,
- // MutationType::TypeForDecl);
- // assert(Last && "TypeForDecl baseline missing at verify time");
- // if (!Last || *Last != Now)
- // Verified |= uint32_t(MutationType::TypeForDecl);
+ // No listener at all for this mutation.
+ // const Type *Now = DeclStateReverter::getRawTypeForDecl(RD);
+ // if (differsFromLast(Tracker, RD, MutationType::TypeForDecl, Now))
+ // Verified |= uint32_t(MutationType::TypeForDecl);
}
if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
- if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
- const auto *Last =
- Tracker.Footprints.mostRecent<SpecializationFootprint>(
- Spec, MutationType::SpecInfo);
- assert(Last && "SpecInfo baseline missing at verify time");
- if (!Last ||
- !DeclStateReverter::compareSpecializationFootprint(*Last, *Spec))
+ if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD))
+ if (differsFromLast(Tracker, Spec, MutationType::SpecInfo,
+ Footprint<SpecializationFootprint>(*Spec)))
Verified |= uint32_t(MutationType::SpecInfo);
- }
} else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
- const auto *Last =
- Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
- RD, MutationType::MemberSpecInfo);
- assert(Last && "MemberSpecInfo baseline missing at verify time");
- if (!Last ||
- !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *RD))
+ if (differsFromLast(Tracker, RD, MutationType::MemberSpecInfo,
+ Footprint<MemberSpecializationFootprint>(*RD)))
Verified |= uint32_t(MutationType::MemberSpecInfo);
}
break;
@@ -498,93 +482,61 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
case DeclShape::Function: {
const auto *FD = cast<FunctionDecl>(D);
if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
- const auto *Last =
- Tracker.Footprints.mostRecent<FunctionSpecializationFootprint>(
- FD, MutationType::SpecInfo);
- assert(Last && "SpecInfo baseline missing at verify time");
- if (!Last || !DeclStateReverter::compareFunctionSpecializationFootprint(
- *Last, *FD))
+ if (differsFromLast(Tracker, FD, MutationType::SpecInfo,
+ Footprint<FunctionSpecializationFootprint>(*FD)))
Verified |= uint32_t(MutationType::SpecInfo);
} else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
- const auto *Last =
- Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
- FD, MutationType::MemberSpecInfo);
- assert(Last && "MemberSpecInfo baseline missing at verify time");
- if (!Last ||
- !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *FD))
+ if (differsFromLast(Tracker, FD, MutationType::MemberSpecInfo,
+ Footprint<MemberSpecializationFootprint>(*FD)))
Verified |= uint32_t(MutationType::MemberSpecInfo);
}
- // ResolvedExceptionSpec/DeducedReturnType firing is itself the
- // confirmation.
- Verified |= FlaggedKinds & (uint32_t(MutationType::ExceptionSpec) |
- uint32_t(MutationType::DeducedReturnType));
- break;
+ break; // Function carries no listener-less kind at all.
}
case DeclShape::Var: {
const auto *VD = cast<VarDecl>(D);
- if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
- if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
- const auto *Last =
- Tracker.Footprints.mostRecent<VarSpecializationFootprint>(
- Spec, MutationType::SpecInfo);
- assert(Last && "SpecInfo baseline missing at verify time");
- if (!Last ||
- !DeclStateReverter::compareVarSpecializationFootprint(*Last, *Spec))
- Verified |= uint32_t(MutationType::SpecInfo);
- }
- } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
- const auto *Last =
- Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
- VD, MutationType::MemberSpecInfo);
- assert(Last && "MemberSpecInfo baseline missing at verify time");
- if (!Last ||
- !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *VD))
- Verified |= uint32_t(MutationType::MemberSpecInfo);
- }
if (FlaggedKinds & uint32_t(MutationType::EvaluatedValue) &&
Tracker.getHiddenMutationTracker().isTrackedFor(
VD, uint32_t(MutationType::EvaluatedValue))) {
- // No chain to compare against -- write-once field, so presence of
- // WasEvaluated=true right now IS the confirmation.
+ // No chain to compare against -- write-once field.
const EvaluatedStmt *Eval = VD->getEvaluatedStmt();
if (Eval && Eval->WasEvaluated)
Verified |= uint32_t(MutationType::EvaluatedValue);
}
+ if (FlaggedKinds & uint32_t(MutationType::SpecInfo)) {
+ if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD))
+ if (differsFromLast(Tracker, Spec, MutationType::SpecInfo,
+ Footprint<VarSpecializationFootprint>(*Spec)))
+ Verified |= uint32_t(MutationType::SpecInfo);
+ } else if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
+ if (differsFromLast(Tracker, VD, MutationType::MemberSpecInfo,
+ Footprint<MemberSpecializationFootprint>(*VD)))
+ Verified |= uint32_t(MutationType::MemberSpecInfo);
+ }
break;
}
case DeclShape::Enum: {
const auto *ED = cast<EnumDecl>(D);
- // if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
- // const Type *Now = DeclStateReverter::getRawTypeForDecl(ED);
- // const auto *Last =
- // Tracker.Footprints.mostRecent<const Type *>(ED,
- // MutationType::TypeForDecl);
- // assert(Last && "TypeForDecl baseline missing at verify time");
- // if (!Last || *Last != Now)
- // Verified |= uint32_t(MutationType::TypeForDecl);
- // }
+ if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
+ // const Type *Now = DeclStateReverter::getRawTypeForDecl(ED);
+ // if (differsFromLast(Tracker, ED, MutationType::TypeForDecl, Now))
+ // Verified |= uint32_t(MutationType::TypeForDecl);
+ }
if (FlaggedKinds & uint32_t(MutationType::MemberSpecInfo)) {
- const auto *Last =
- Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
- ED, MutationType::MemberSpecInfo);
- assert(Last && "MemberSpecInfo baseline missing at verify time");
- if (!Last ||
- !DeclStateReverter::compareMemberSpecializationFootprint(*Last, *ED))
+ if (differsFromLast(Tracker, ED, MutationType::MemberSpecInfo,
+ Footprint<MemberSpecializationFootprint>(*ED)))
Verified |= uint32_t(MutationType::MemberSpecInfo);
}
break;
}
case DeclShape::Template: {
- // SpecializationAdded is known directly from
- // AddedCXXTemplateSpecialization firing, so
- // always confirmed when flagged.
- Verified |= FlaggedKinds & uint32_t(MutationType::SpecializationAdded);
+ // Use the canonical key. commitTemplate() tracks and settles CommonBase
+ // modifications, and CommonPtr is shared across the redeclaration chain.
+ // Any modification to CommonPtr is reflected in the canonical declaration.
const auto *RT = cast<RedeclarableTemplateDecl>(D)->getCanonicalDecl();
if (FlaggedKinds & uint32_t(MutationType::TemplateCommon) &&
Tracker.getHiddenMutationTracker().isTrackedFor(
RT, uint32_t(MutationType::TemplateCommon))) {
- // No chain to compare against -- write-once, so Common's own
- // existence right now IS the confirmation.
+ // No chain to compare against -- write-once.
if (DeclStateReverter::isCommonPtrValid(*RT))
Verified |= uint32_t(MutationType::TemplateCommon);
}
@@ -595,40 +547,71 @@ uint32_t PTUMutationActions::verifyMutationFor(const Decl *D, DeclShape S,
if (DeclStateReverter::isTemplateCanonInjectedTSTValid(CTD))
Verified |= uint32_t(MutationType::CanonInjectedTST);
}
- } break;
+ break;
+ }
case DeclShape::Typedef: {
const auto *TD = cast<TypedefNameDecl>(D);
if (FlaggedKinds & uint32_t(MutationType::TypeForDecl) &&
Tracker.getHiddenMutationTracker().isTrackedFor(
TD, uint32_t(MutationType::TypeForDecl))) {
- // No chain to compare against -- write-once field (unlike
- // TagDecl's TypeForDecl, this one is guarded by ASTContext's own
- // `if (Decl->TypeForDecl) return`, never overwritten a second
- // time), so non-null right now IS the confirmation.
- // if (DeclStateReverter::getRawTypeForDecl(TD))
- // Verified |= uint32_t(MutationType::TypeForDecl);
+ // No chain to compare against -- write-once field.
+ // if (DeclStateReverter::getRawTypeForDecl(TD))
+ // Verified |= uint32_t(MutationType::TypeForDecl);
}
- } break;
+ break;
+ }
case DeclShape::None:
- break; // never flagged in the first place.
+ break;
}
return Verified;
}
template <typename OnConfirmedFn>
void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
- // llvm::SmallVector<std::pair<const Decl *, uint32_t>, 8> Confirmed;
for (auto &Entry : Active) {
const Decl *D = Entry.getFirst();
uint32_t StillOpen = Entry.getSecond();
DeclShape S = classifyShape(D);
- if (uint32_t Newly = Act.verifyMutationFor(D, S, StillOpen)) {
+ if (uint32_t Newly = Act.confirmMutation(D, S, StillOpen)) {
OnConfirmed(D, S, Newly);
- // Confirmed.emplace_back(D, Newly);
}
}
- // for (auto &[D, Newly] : Confirmed)
- // settle(D, Newly);
+}
+
+// These kinds have listeners, but the listener alone can’t reliably confirm
+// a real change. So, compare the current value with the last known value,
+// just like we do for kinds without listeners.
+//
+// These kinds are also checked by the sweep. This is needed because the
+// listener can report a change that didn’t happen, or fail to fire when a
+// real change did happen. The sweep catches the second case.
+static constexpr uint32_t KindsNeedingVerification =
+ uint32_t(MutationType::SpecInfo) | uint32_t(MutationType::MemberSpecInfo);
+
+/// Start tracking D for the mutation kinds that need live verification.
+/// Called when a new mutation is committed or an undone mutation is restored.
+/// The callers have already established which kinds need tracking, so no
+/// additional DeclNeedingTracking check is needed.
+void PTUMutationActions::registerLiveVerification(const Decl *D,
+ uint32_t Kinds) {
+ if (uint32_t Live = Kinds & KindsNeedingVerification)
+ HiddenMutationTracker.track(D, Live);
+}
+
+/// Stop tracking Decl D when its mutation no longer needs live verification.
+/// Called from verifyMutations() and the !IsNew SpecInfo/MemberSpecInfo
+/// paths in commitDecl. Re-check DeclNeedingTracking to match the same
+/// "is this still open?" condition used when D is first discovered:
+/// keep tracking if a later PTU can still update it; otherwise settle it.
+void PTUMutationActions::settleIfClosed(const Decl *D, DeclShape S,
+ uint32_t Confirmed) {
+ uint32_t Live = Confirmed & KindsNeedingVerification;
+ if (!Live)
+ return;
+ uint32_t StillOpen = DeclNeedingTracking(S, D) & Live;
+ uint32_t Closed = Live & ~StillOpen;
+ if (Closed && HiddenMutationTracker.isTrackedFor(D, Closed))
+ HiddenMutationTracker.settle(D, Closed);
}
/// Verify recorded mutations and remove any spurious ones. If none of a
@@ -636,10 +619,15 @@ void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
/// restore() does not pop chain entries that were never written.
void PTUStateInfo::verifyMutations(PTUMutationActions &Actions) {
Mutations.remove_if([&](auto &Entry) -> bool {
- uint32_t Confirmed = Actions.verifyMutationFor(Entry.first, Entry.second.S,
- Entry.second.MutationType);
- Entry.second.MutationType = Confirmed;
- return Confirmed == 0;
+ const Decl *D = Entry.first;
+ MutationRecord &Rec = Entry.second;
+ uint32_t Flagged = Rec.MutationType & KindsNeedingVerification;
+ if (!Flagged)
+ return false; // nothing here needs live verification.
+ uint32_t Confirmed = Actions.confirmMutation(D, Rec.S, Flagged);
+ Actions.settleIfClosed(D, Rec.S, Confirmed);
+ Rec.MutationType = (Rec.MutationType & ~Flagged) | Confirmed;
+ return Rec.MutationType == 0;
});
}
@@ -702,61 +690,55 @@ class DeclStateUnlinkPolicy {
}
void process(const Decl *D) {
+ Reverter.detachFromExternCLookup(
+ const_cast<Decl *>(D), Tracker.getASTContext().getExternCContextDecl(),
+ ID);
+ if (auto *ND = dyn_cast<NamedDecl>(const_cast<Decl *>(D)))
+ DeclStateReverter::removeFromIdResolver(Tracker.getSema(), ND);
+
const Decl *Canon = D->getCanonicalDecl();
- /// Keyed on the canonical decl: every redeclaration shares one
- /// RedeclLink, so truncating twice would step past the intended survivor.
+ // Whether Candidate's own DefinitionData belongs to this PTU and needs
+ // detaching.
+ auto needsDefDataDetach = [&](const Decl *Candidate) {
+ const auto *RD = dyn_cast<CXXRecordDecl>(Candidate);
+ const CXXRecordDecl *Def = RD ? RD->getDefinition() : nullptr;
+ return Def && Tracker.isFromThisPTU(Def, ID);
+ };
+
+ /// Keyed by the canonical declaration: all redeclarations share a single
+ /// RedeclLink, so truncating more than once would advance past the intended
+ /// surviving declaration.
bool DetachFromRedecl = !RepairedChains.count(Canon) &&
D->getPreviousDecl() &&
!Tracker.isFromThisPTU(Canon, ID);
+ if (needsDefDataDetach(D))
+ Reverter.detachDefData(D);
+
// Keyed on the primary context, a different granularity: a reopened
// namespace's redeclarations all resolve to the same primary.
- const DeclContext *DC = D->getDeclContext()->getPrimaryContext();
- bool DetachFromDC = TouchedDC.contains(DC);
-
- bool DetachFromLexicalChain =
- D->isImplicit() &&
- !RepairedLexicalContexts.count(D->getLexicalDeclContext());
-
- bool DetachDefData = false;
- bool HasTemplated = false;
- bool TemplatedDetachDefData = false;
- if (const auto *RT = dyn_cast<RedeclarableTemplateDecl>(D)) {
- const NamedDecl *T = RT->getTemplatedDecl();
- HasTemplated = true;
- if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(T)) {
- if (const CXXRecordDecl *Def = RD->getDefinition();
- Def && Tracker.isFromThisPTU(Def, ID))
- TemplatedDetachDefData = true;
- }
- } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
- if (const CXXRecordDecl *Def = RD->getDefinition();
- Def && Tracker.isFromThisPTU(Def, ID))
- DetachDefData = true;
- }
-
- if (DetachDefData)
- Reverter.detachDefData(D);
- if (DetachFromDC)
+ if (const DeclContext *DC = D->getDeclContext()->getPrimaryContext();
+ TouchedDC.contains(DC))
Reverter.detachFromDCLookup(const_cast<Decl *>(D), ID);
+
if (DetachFromRedecl) {
RepairedChains.insert(Canon);
Reverter.detachFromRedeclChain(D, ID);
}
- if (HasTemplated) {
- const Decl *T = cast<RedeclarableTemplateDecl>(D)->getTemplatedDecl();
- if (TemplatedDetachDefData)
- Reverter.detachDefData(T);
+ if (const auto *RT = dyn_cast<RedeclarableTemplateDecl>(D)) {
+ const NamedDecl *Templated = RT->getTemplatedDecl();
+ if (needsDefDataDetach(Templated))
+ Reverter.detachDefData(Templated);
if (DetachFromRedecl)
- Reverter.detachFromRedeclChain(T, ID);
+ Reverter.detachFromRedeclChain(Templated, ID);
}
- if (DetachFromLexicalChain) {
- Reverter.repairLexicalChain(
- *const_cast<DeclContext *>(D->getLexicalDeclContext()), ID);
- RepairedLexicalContexts.insert(D->getLexicalDeclContext());
+ if (const DeclContext *LexicalDC = D->getLexicalDeclContext();
+ D->isImplicit() && !RepairedLexicalContexts.count(LexicalDC)) {
+ Reverter.repairLexicalChain(*const_cast<DeclContext *>(LexicalDC), ID);
+ RepairedLexicalContexts.insert(LexicalDC);
}
}
};
@@ -814,23 +796,35 @@ void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
if (Spec) {
Tracker.commitFootprint(
Spec, MutationType::SpecInfo, ID,
- DeclStateReverter::createSpecializationFootprint(*Spec));
+ DeclStateReverter::createFootprint<SpecializationFootprint>(*Spec));
// Member declarations are instantiated eagerly with the class
// definition regardless of TSK; only their definitions defer. Walk
// them now so each member's MemberSpecializationInfo is tracked from
// the moment it exists.
if (IsNew && Spec->getSpecializationKind() == TSK_ImplicitInstantiation)
commitMembers(ID, cast<DeclContext>(Spec));
+ if (IsNew)
+ registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
+ else
+ settleIfClosed(Spec, DeclShape::Class,
+ uint32_t(MutationType::SpecInfo));
}
}
if (Rec.has(ClassMutation::MemberSpecInfo)) {
// A nested member of a class template -- its MSI's kind or
// point-of-instantiation changed.
- if (RD->getMemberSpecializationInfo())
+ if (RD->getMemberSpecializationInfo()) {
Tracker.commitFootprint(
RD, MutationType::MemberSpecInfo, ID,
- DeclStateReverter::createMemberSpecializationFootprint(*RD));
+ DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
+ *RD));
+ if (IsNew)
+ registerLiveVerification(RD, uint32_t(MutationType::MemberSpecInfo));
+ else
+ settleIfClosed(RD, DeclShape::Class,
+ uint32_t(MutationType::MemberSpecInfo));
+ }
}
}
@@ -862,14 +856,26 @@ void PTUMutationActions::commitFunction(PTUID ID, const FunctionDecl *FD,
"SpecInfoChanged on a function with no specialization info");
Tracker.commitFootprint(
FD, MutationType::SpecInfo, ID,
- DeclStateReverter::createFunctionSpecializationFootprint(*FD));
+ DeclStateReverter::createFootprint<FunctionSpecializationFootprint>(
+ *FD));
+ if (IsNew)
+ registerLiveVerification(FD, uint32_t(MutationType::SpecInfo));
+ else
+ settleIfClosed(FD, DeclShape::Function, uint32_t(MutationType::SpecInfo));
}
if (Rec.has(MutationType::MemberSpecInfo)) {
- if (FD->getMemberSpecializationInfo())
+ if (FD->getMemberSpecializationInfo()) {
Tracker.commitFootprint(
FD, MutationType::MemberSpecInfo, ID,
- DeclStateReverter::createMemberSpecializationFootprint(*FD));
+ DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
+ *FD));
+ if (IsNew)
+ registerLiveVerification(FD, uint32_t(MutationType::MemberSpecInfo));
+ else
+ settleIfClosed(FD, DeclShape::Function,
+ uint32_t(MutationType::MemberSpecInfo));
+ }
}
}
@@ -909,14 +915,25 @@ void PTUMutationActions::commitVar(PTUID ID, const VarDecl *VD,
const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
Tracker.commitFootprint(
Spec, MutationType::SpecInfo, ID,
- DeclStateReverter::createVarSpecializationFootprint(*Spec));
+ DeclStateReverter::createFootprint<VarSpecializationFootprint>(*Spec));
+ if (IsNew)
+ registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
+ else
+ settleIfClosed(Spec, DeclShape::Var, uint32_t(MutationType::SpecInfo));
}
if (Rec.has(MutationType::MemberSpecInfo)) {
- if (VD->getMemberSpecializationInfo())
+ if (VD->getMemberSpecializationInfo()) {
Tracker.commitFootprint(
VD, MutationType::MemberSpecInfo, ID,
- DeclStateReverter::createMemberSpecializationFootprint(*VD));
+ DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
+ *VD));
+ if (IsNew)
+ registerLiveVerification(VD, uint32_t(MutationType::MemberSpecInfo));
+ else
+ settleIfClosed(VD, DeclShape::Var,
+ uint32_t(MutationType::MemberSpecInfo));
+ }
}
}
@@ -941,24 +958,23 @@ void PTUMutationActions::commitEnum(PTUID ID, const EnumDecl *ED,
if (Rec.has(MutationType::MemberSpecInfo)) {
// A scoped member enumeration of a class template -- instantiated with
// the enclosing specialization, carrying MSI back to the pattern enum.
- if (ED->getMemberSpecializationInfo())
+ if (ED->getMemberSpecializationInfo()) {
Tracker.commitFootprint(
ED, MutationType::MemberSpecInfo, ID,
- DeclStateReverter::createMemberSpecializationFootprint(*ED));
+ DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
+ *ED));
+ if (IsNew)
+ registerLiveVerification(ED, uint32_t(MutationType::MemberSpecInfo));
+ else
+ settleIfClosed(ED, DeclShape::Enum,
+ uint32_t(MutationType::MemberSpecInfo));
+ }
}
}
void PTUMutationActions::commitTemplate(PTUID ID,
const RedeclarableTemplateDecl *RT,
MutationRecord &Rec, bool IsNew) {
- // HiddenMutationTracker keeps both flags per declaration, and Cur.Mutations
- // (Rec, per PTU) already tells us whether this PTU confirmed it, which is
- // exactly what restoreTemplate needs, just like restoreVar. Handle both
- // flags the same way: IsNew registers the declaration for a future sweep;
- // otherwise, this is the confirmation from SweepTracker::sweep()'s
- // OnConfirmed callback. Once either flag is true, it is final—
- // Common/CanonInjectedTST won't reset it on their own—so settle it now
- // instead of waiting for another sweep.
const RedeclarableTemplateDecl *RTCanon = RT->getCanonicalDecl();
if (Rec.has(MutationType::TemplateCommon)) {
if (IsNew)
@@ -992,21 +1008,17 @@ void PTUMutationActions::commitTypedef(PTUID ID, const TypedefNameDecl *TD,
void PTUMutationActions::commitDecl(PTUID ID, const Decl *D,
MutationRecord &Rec, bool IsNew) {
switch (Rec.S) {
-#define PTU_SHAPE(NAME, TYPE) \
+#define DECL_SHAPE(NAME, TYPE) \
case DeclShape::NAME: \
commit##NAME(ID, cast<TYPE>(D), Rec, IsNew); \
break;
- PTU_SHAPES
-#undef PTU_SHAPE
+ DECL_SHAPES
+#undef DECL_SHAPE
case DeclShape::None:
break;
}
}
-// respect the LIFO so only current inside map not commited can be commited
-// not randon PTUID
-// global map info shouldn't be commited before only added here. not note*
-// time.
void PTUMutationActions::commit(TranslationUnitDecl *ThisTU) {
PTUStateInfo &Cur = Tracker.current();
assert(!Cur.Commited);
@@ -1068,8 +1080,9 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
if (const SpecializationFootprint *Restored =
Tracker.getFootprints().mostRecent<SpecializationFootprint>(
Spec, MutationType::SpecInfo))
- DeclStateReverter::restoreSpecializationFootprint(
+ DeclStateReverter::restoreFootprint<SpecializationFootprint>(
*Restored, *const_cast<ClassTemplateSpecializationDecl *>(Spec));
+ registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
}
if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -1079,8 +1092,9 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
if (const MemberSpecializationFootprint *Restored =
Tracker.getFootprints().mostRecent<MemberSpecializationFootprint>(
RD, MutationType::MemberSpecInfo))
- DeclStateReverter::restoreMemberSpecializationFootprint(
+ DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
*Restored, *const_cast<CXXRecordDecl *>(RD));
+ registerLiveVerification(RD, uint32_t(MutationType::MemberSpecInfo));
}
}
@@ -1111,19 +1125,21 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
assert(FD->getTemplateSpecializationKind() != TSK_Undeclared &&
"SpecInfoChanged on a function with no specialization info");
if (const auto *Restored =
- Tracker.Footprints.mostRecent<FunctionSpecializationFootprint>(
+ Tracker.getFootprints().mostRecent<FunctionSpecializationFootprint>(
FD, MutationType::SpecInfo))
- DeclStateReverter::restoreFunctionSpecializationFootprint(
+ DeclStateReverter::restoreFootprint<FunctionSpecializationFootprint>(
*Restored, *const_cast<FunctionDecl *>(FD));
+ registerLiveVerification(FD, uint32_t(MutationType::SpecInfo));
}
if (Rec.has(MutationType::MemberSpecInfo)) {
assert(FD->getMemberSpecializationInfo());
if (const auto *Restored =
- Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
+ Tracker.getFootprints().mostRecent<MemberSpecializationFootprint>(
FD, MutationType::MemberSpecInfo))
- DeclStateReverter::restoreMemberSpecializationFootprint(
+ DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
*Restored, *const_cast<FunctionDecl *>(FD));
+ registerLiveVerification(FD, uint32_t(MutationType::MemberSpecInfo));
}
}
@@ -1171,56 +1187,46 @@ void PTUMutationActions::restoreTypedef(PTUID ID, const TypedefNameDecl *TD,
void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
MutationRecord &Rec) {
-
- using VarMutation = MutationType;
-
- // EvaluatedStmt::WasEvaluated is write-once, so its previous state is always
- // null here. If it made it into Rec, this PTU had confirmed it as evaluated.
- // Reverting it is therefore just a reset, not a snapshot restore – the
- // original initializer expression in Eval->Value stays untouched; we only
- // clear the cached result.
- if (Rec.has(VarMutation::EvaluatedValue)) {
- // VD itself survives this rollback (only its cached value was this
- // PTU's doing) -- reset the live field, and re-register with
- // SweepTracker since DeclNeedingTracking will flag this as open
- // again and some later PTU could re-trigger evaluation.
+ // EvaluatedStmt::WasEvaluated is write-once, so a recorded entry means this
+ // PTU confirmed the evaluation.
+ if (Rec.has(MutationType::EvaluatedValue)) {
+ // VD cached evaluation came from this PTU.
+ // Reset it and track it again so a later PTU can re-trigger evaluation.
if (EvaluatedStmt *Eval = VD->getEvaluatedStmt()) {
Eval->WasEvaluated = false;
Eval->Evaluated = APValue();
}
- HiddenMutationTracker.track(VD, uint32_t(VarMutation::EvaluatedValue));
+ HiddenMutationTracker.track(VD, uint32_t(MutationType::EvaluatedValue));
}
- if (Rec.has(VarMutation::DefinitionInstantiate)) {
+ if (Rec.has(MutationType::DefinitionInstantiate)) {
DeclStateReverter::revertDefinitionArrival(*const_cast<VarDecl *>(VD));
}
- if (Rec.has(VarMutation::SpecInfo)) {
- // Unlike functions, a variable specialization IS a distinct type.
+ if (Rec.has(MutationType::SpecInfo)) {
const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
if (const auto *Restored =
Tracker.getFootprints().mostRecent<VarSpecializationFootprint>(
Spec, MutationType::SpecInfo))
- DeclStateReverter::restoreVarSpecializationFootprint(
+ DeclStateReverter::restoreFootprint<VarSpecializationFootprint>(
*Restored, *const_cast<VarTemplateSpecializationDecl *>(Spec));
+ registerLiveVerification(VD, uint32_t(MutationType::SpecInfo));
}
- if (Rec.has(VarMutation::MemberSpecInfo)) {
+ if (Rec.has(MutationType::MemberSpecInfo)) {
assert(VD->getMemberSpecializationInfo());
if (const auto *Restored =
Tracker.Footprints.mostRecent<MemberSpecializationFootprint>(
VD, MutationType::MemberSpecInfo))
- DeclStateReverter::restoreMemberSpecializationFootprint(
+ DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
*Restored, *const_cast<VarDecl *>(VD));
+ registerLiveVerification(VD, uint32_t(MutationType::MemberSpecInfo));
}
}
void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
MutationRecord &Rec) {
- // Like a class, an EnumDecl's TypeForDecl is shared across its whole
- // redeclaration chain (`enum class E : int;` forward-declared, then
- // defined later), so every redecl's cached pointer needs checking.
if (Rec.has(MutationType::TypeForDecl)) {
// for (const TagDecl *Redecl : ED->redecls()) {
// const auto *Last = Tracker.Footprints.mostRecent<const Type *>(
@@ -1235,36 +1241,52 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
// *>(ED));
if (Rec.has(MutationType::MemberSpecInfo)) {
- // A scoped member enumeration of a class template -- instantiated with
- // the enclosing specialization, carrying MSI back to the pattern enum.
assert(ED->getMemberSpecializationInfo());
if (const auto *Restored =
Tracker.getFootprints().mostRecent<MemberSpecializationFootprint>(
ED, MutationType::MemberSpecInfo))
- DeclStateReverter::restoreMemberSpecializationFootprint(
+ DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
*Restored, *const_cast<EnumDecl *>(ED));
+
+ registerLiveVerification(ED, uint32_t(MutationType::MemberSpecInfo));
}
}
void PTUMutationActions::restoreDecl(PTUID ID, const Decl *D,
MutationRecord &Rec) {
switch (Rec.S) {
-#define PTU_SHAPE(NAME, TYPE) \
+#define DECL_SHAPE(NAME, TYPE) \
case DeclShape::NAME: \
restore##NAME(ID, cast<TYPE>(D), Rec); \
break;
- PTU_SHAPES
-#undef PTU_SHAPE
+ DECL_SHAPES
+#undef DECL_SHAPE
case DeclShape::None:
break;
}
}
-#undef PTU_SHAPES
+#undef DECL_SHAPES
+
+void PTUMutationActions::restoreSpecialMemberCache(PTUID ID) {
+ PTUStateInfo &Cur = Tracker.current();
+ if (!Cur.HadImplicitCXXMember)
+ return;
+
+ Sema &SemaRef = Tracker.getSema();
+ llvm::SmallVector<Sema::SpecialMemberCacheKey, 8> Stale;
+ for (auto &Entry : SemaRef.SpecialMemberCache) {
+ CXXMethodDecl *MD = Entry.second.getMethod();
+ if (MD && (Tracker.isFromThisPTU(MD, ID) || Cur.ImplicitDecls.contains(MD)))
+ Stale.push_back(Entry.first);
+ }
+ for (const auto &Key : Stale)
+ SemaRef.SpecialMemberCache.erase(Key);
+}
void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
PTUStateInfo &Cur = Tracker.current();
- assert(ThisTU == Cur.ThisPTU);
- // TranslationUnitDecl *ThisTU = Cur.ThisPTU;
+ assert((!Cur.Commited || ThisTU == Cur.ThisPTU) &&
+ "MostRecentTU doesn't match the TU recorded at commit time");
PTUID ID = Cur.ID;
if (Cur.Commited)
Tracker.undoLastEntries();
@@ -1288,24 +1310,11 @@ void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
walkDecls(ThisTU, Policy);
- // Sema::SpecialMemberCache (public) caches, per (RD, kind+qualifiers), the
- // CXXMethodDecl* a prior LookupSpecialMember() call resolved to -- and on
- // a cache hit it returns that decl directly, with no re-validation at all
- // (SemaLookup.cpp). Any entry whose cached method was created by the PTU
- // being rolled back is now stale, so purge it here rather than trying to
- // reconstruct which of the (RD, kind) keys it could occupy.
- {
- Sema &SemaRef = Tracker.getSema();
- llvm::SmallVector<Sema::SpecialMemberCacheKey, 8> Stale;
- for (auto &Entry : SemaRef.SpecialMemberCache) {
- CXXMethodDecl *MD = Entry.second.getMethod();
- if (MD &&
- (Tracker.isFromThisPTU(MD, ID) || Cur.ImplicitDecls.contains(MD)))
- Stale.push_back(Entry.first);
- }
- for (const auto &Key : Stale)
- SemaRef.SpecialMemberCache.erase(Key);
- }
+ // SpecialMemberCache stores the CXXMethodDecl* resolved by a previous
+ // LookupSpecialMember() call and returns it directly on a cache hit.
+ // If that method was created by a PTU that is being rolled back, the entry
+ // is stale, so remove it here.
+ restoreSpecialMemberCache(ID);
// Must be last: Cur/ID/ThisTU are all references into (or derived from)
// the PTU this call retires, and popCurrentPTU() destroys that entry.
@@ -1331,16 +1340,14 @@ void PTUMutationRecorder::CompletedTagDefinition(const TagDecl *D) {
Cur.ImplicitDecls.insert(D);
return;
}
- // Predates this PTU: a genuine cross-PTU mutation -- needs the full
- // definition-data footprint captured in commit().
- // Cur.TouchedTagDecls.insert(D);
+
DeclShape S = classifyShape(D);
- /// doing this for memspec because we are not sure that if this cause member
- /// spec mutation or not we can't put compare check here as well since
- /// notifer never guarranty that will called before mutation or later. so
- /// this check about mutation will be handled in verification layer
Cur.noteMutated(D, S, MutationType::DefinitionInstantiate);
+
+ /// Handle this for MemberSpec because the notifier does not guarantee whether
+ /// it fires before or after the mutation. We cannot reliably compare the
+ /// state here, so mutation detection is deferred to the verification layer.
if (S == DeclShape::Enum) {
if (const EnumDecl *ED = dyn_cast<EnumDecl>(D);
ED && ED->getMemberSpecializationInfo())
@@ -1362,10 +1369,7 @@ void PTUMutationRecorder::AddedVisibleDecl(const DeclContext *DC,
if (Tracker.isFromThisPTU(DC, Cur.ID) || Cur.TouchedDC.contains(DC))
return;
- // auto Origin =
- // Tracker.getPTUSlabCheckpoints().attribute(Tracker.Ctx, DC);
- // assert(Origin && "must have existing PTUID");
- // assert(*Origin != Cur.ID);
+
Cur.TouchedDC.insert(DC);
}
@@ -1379,8 +1383,6 @@ void PTUMutationRecorder::AddedCXXImplicitMember(const CXXRecordDecl *RD,
if (D->isImplicit())
Cur.ImplicitDecls.insert(D);
- // cover this part as well
- // if (RD->MemberInfo and D is implcit plus has MSIInfo)
// cover only not from this ptu.
if (Tracker.isFromThisPTU(RD, Cur.ID))
@@ -1467,9 +1469,6 @@ void PTUMutationRecorder::InstantiationRequested(const ValueDecl *D) {
if (FD->getMemberSpecializationInfo())
K = MutationType::MemberSpecInfo;
else if (FD->getTemplateSpecializationKind() != TSK_Undeclared)
- // A function template specialization is a plain FunctionDecl
- // carrying FunctionTemplateSpecializationInfo -- there is no
- // distinct decl type to dyn_cast to, unlike the variable case.
K = MutationType::SpecInfo;
else
return; // neither -- nothing this notifier can be about
@@ -1488,11 +1487,7 @@ void PTUMutationRecorder::InstantiationRequested(const ValueDecl *D) {
Cur.noteMutated(D, S, K);
}
-// Each (Owner, Kind) key written by this PTU has its trailing entries
-// removed in one uniform call. There’s no need for per-node-type
-// release() or pool bookkeeping: FootprintStore’s SmallVector-per-key
-// storage manages the memory normally. Once a key’s history is empty,
-// removeFrom erases the key entirely, just as it always has.
+// Remove all entries for each (Owner, Kind) key written by this PTU.
void IncrementalStateTracker::undoLastEntries() {
PTUStateInfo &Cur = current();
PTUID ID = Cur.ID;
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index fac67bf00ed14..2c5a4d8489dc1 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -333,8 +333,6 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
// Lookup alone is not enough: the redeclaration chain still reaches these.
withdrawMostRecentTU(MostRecentTU);
- // RepairRedeclChain(MostRecentTU, MostRecentTU);
- // S.getASTContext().setTranslationUnitDecl(MostRecentTU->getPreviousDecl());
}
PartialTranslationUnit &
>From e464102e3a8b32a0e06990333d2121dd1ae189b1 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 30 Sep 2026 14:28:31 +0530
Subject: [PATCH 11/13] Make improvements and add meaningful comments
---
.../include/clang/Interpreter/ErrorRecovery.h | 72 ++-
clang/lib/Interpreter/ErrRecovery.cpp | 528 +++++++++++-------
clang/lib/Interpreter/Interpreter.cpp | 4 +-
llvm/include/llvm/Support/Allocator.h | 95 ++--
4 files changed, 389 insertions(+), 310 deletions(-)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index b8365efc41968..85f7ffef1e8a0 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -7,6 +7,8 @@
//
//===----------------------------------------------------------------------===//
//
+// This header provides the main error-recovery component for supporting
+// error recovery in clang-repl.
//
//===----------------------------------------------------------------------===//
@@ -27,16 +29,8 @@ class Sema;
/// Index of a per PTU State.
using PTUID = unsigned;
-// A snapshot of the handful of DefinitionData bits/bitfields that can
-// change after a class (or class template specialization -- the two are
-// handled identically here since a specialization is non-dependent by the
-// time it's instantiated) is otherwise "done" being defined.
-//
-// These fields only move when the compiler generates implicit special
-// members.
-//
-// This footprint exists purely to undo the effects of implicit special
-// member generation.
+/// A snapshot of the DefinitionData bits and bitfields that can change
+/// after a class gets a special implicit member added.
struct DefinitionDataFootprint {
#define FIELD(Name, Width, Merge) unsigned Name : Width;
#include "clang/AST/CXXRecordDeclDefinitionBits.def"
@@ -286,9 +280,7 @@ class DeclStateReverter {
FP.restore(Owner);
}
- static void restoreDefinitionAndRevertDC(CXXRecordDecl &RD);
-
- static void revertDefinitionArrival(Decl &D);
+ static void revertDefinitionArrival(Decl *D);
// True if Common could still be created later -- i.e. nobody has called
// getCommonPtr() anywhere in this template's redecl chain yet.
@@ -314,10 +306,7 @@ class DeclStateReverter {
}
// Specializations are appended in commit order, and rollback is strictly
- // LIFO (only the most recently committed PTU is ever rolled back). So all
- // specializations added by a given PTU form a trailing run at the back of
- // the FoldingSetVector. Popping entries while the back entry belongs to
- // this PTU is therefore sufficient.
+ // LIFO (only the most recently committed PTU is ever rolled back).
static void removeSpecializations(PTUCheckpointLedger &Ledger,
const RedeclarableTemplateDecl *TD,
PTUID ID);
@@ -441,7 +430,7 @@ class PTUCheckpointLedger {
struct MutationRecord {
enum class DeclShape : uint16_t {
None = 0,
- // Base shapes -- what the decl fundamentally IS. Exactly one is set.
+ // Base shapes -- what the decl fundamentally is. Exactly one is set.
Class = 1 << 0, // CXXRecordDecl/TagDecl: DefinitionData, TypeForDecl
Function = 1 << 1, // FunctionDecl: exception spec, deduced return, body
Var = 1 << 2, // VarDecl: cached constant-eval result
@@ -538,14 +527,7 @@ struct PTUStateInfo {
llvm::SmallPtrSet<const Decl *, 4> ImplicitDecls;
- /// Set exactly when AddedCXXImplicitMember fires this PTU -- narrower
- /// than ImplicitDecls above, which is a shared superset populated by
- /// many unrelated listener paths too (template instantiation, etc.).
- /// This is the one listener whose firing can leave a stale
- /// Sema::SpecialMemberCache entry behind, so it's what
- /// purgeStaleSpecialMemberCache actually needs to gate on -- gating on
- /// ImplicitDecls instead would trigger that scan on almost every PTU,
- /// defeating the point of the gate.
+ /// Set exactly when AddedCXXImplicitMember fires this PTU.
bool HadImplicitCXXMember = false;
/// here touched info mean other this belongs to other PTUs;
@@ -609,7 +591,7 @@ class SweepTracker {
return It != Active.end() && (It->second & Flag);
}
- void settle(const Decl *D, uint32_t Flag) {
+ void untrack(const Decl *D, uint32_t Flag) {
auto It = Active.find(D);
if (It == Active.end())
return;
@@ -618,6 +600,16 @@ class SweepTracker {
Active.erase(It);
}
+ // removes every Active entry whose decl matches Predicate.
+ template <typename PredicateFn> void untrackIf(PredicateFn Predicate) {
+ llvm::SmallVector<const Decl *, 8> Stale;
+ for (auto &Entry : Active)
+ if (Predicate(Entry.getFirst()))
+ Stale.push_back(Entry.getFirst());
+ for (const Decl *D : Stale)
+ Active.erase(D);
+ }
+
// Call at commit/restore time. OnConfirmed is invoked as
// (const Decl *D, DeclShape S, uint32_t ConfirmedKinds) for every decl
// that had something newly confirmed this sweep.
@@ -640,8 +632,7 @@ class SweepTracker {
// - For the current PTU, collect newly added declarations that are
// modifiable/visible to other PTUs. Local-only declarations are not exposed.
//
-// In short, this keeps the shared declaration state in sync across PTUs while
-// keeping declarations that are local to the current PTU private.
+// In short, this keeps the shared declaration state in sync across PTUs.
class IncrementalStateTracker {
private:
ASTContext &Ctx;
@@ -666,10 +657,9 @@ class IncrementalStateTracker {
IncrementalStateTracker(ASTContext &Ctx, Sema &S)
: Ctx(Ctx), SemaRef(S), PTUSlabCheckpoints(Ctx) {}
- // Writes a new value and records that this PTU touched (Owner, K), so
- // undoLastEntries() knows what to remove if the PTU is rolled back
- // without committing. mostRecent() doesn't need any bookkeeping, so it
- // reads directly from Footprints via Tracker.Footprints.mostRecent<T>(...).
+ // Writes a new value to FootprintStore and records that this PTU touched
+ // (Owner, K), so undoLastEntries() knows what to remove if the PTU is rolled
+ // back without committing.
template <typename T>
void commitFootprint(const Decl *Owner, MutationType K, PTUID ID,
const T &Fresh) {
@@ -728,15 +718,18 @@ class PTUMutationActions {
Reverter(Tracker.getPTUSlabCheckpoints()) {}
template <typename DeclStatePolicyT>
- void walkDecls(const DeclContext *DC, DeclStatePolicyT &Proxy);
+ void walkDecls(const DeclContext *DC, DeclStatePolicyT &Policy);
uint32_t DeclNeedingTracking(DeclShape S, const Decl *D);
uint32_t confirmMutation(const Decl *D, DeclShape S, uint32_t FlaggedKinds);
- void registerLiveVerification(const Decl *D, uint32_t Kinds);
+ void trackForSweep(const Decl *D, uint32_t Kinds);
+
+ void untrackIfClosed(const Decl *D, DeclShape S, uint32_t Confirmed);
- void settleIfClosed(const Decl *D, DeclShape S, uint32_t Confirmed);
+ void syncSweepTracking(const Decl *D, DeclShape S, uint32_t Kinds,
+ bool IsNew);
private:
void commitMembers(PTUID ID, const DeclContext *Members);
@@ -783,10 +776,9 @@ class PTUMutationActions {
void restore(TranslationUnitDecl *MostRecentTU);
private:
- // Sema::SpecialMemberCache, per (RD,
- // kind+qualifiers), the CXXMethodDecl* a prior LookupSpecialMember()
- // call resolved to, with no re-validation on a cache hit. remove every
- // entry whose cached method belongs to the PTU being rolled back.
+ // Sema::SpecialMemberCache stores the CXXMethodDecl* resolved by a previous
+ // LookupSpecialMember() call for each (RD, kind+qualifiers). Remove entries
+ // whose cached method belongs to the PTU being rolled back.
void restoreSpecialMemberCache(PTUID ID);
};
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index 509d0d0a40668..fc9182f16aaa0 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -59,29 +59,52 @@ void DeclStateReverter::restoreDefinitionDataFootprint(
#include "clang/AST/CXXRecordDeclDefinitionBits.def"
}
-void DeclStateReverter::restoreDefinitionAndRevertDC(CXXRecordDecl &RD) {
- // 1. DefinitionData pointer -- back to null.
- RD.DefinitionData = nullptr;
-
- // 2. DeclContext member list -- back to empty.
- clearDeclContextChain(RD);
- DeclContext *Primary = RD.getDeclContext()->getPrimaryContext();
- if (StoredDeclsMap *Map = Primary->getLookupPtr())
- Map->clear();
-
- // 3. TagDecl completion bits -- back to "never started". Flipped by
- // startDefinition()/completeDefinition().
- clearBeingDefined(RD);
- RD.setCompleteDefinition(false);
-}
-
-void DeclStateReverter::revertDefinitionArrival(Decl &D) {
- if (auto *FD = dyn_cast<FunctionDecl>(&D))
+void DeclStateReverter::revertDefinitionArrival(Decl *D) {
+ if (auto *FD = dyn_cast<FunctionDecl>(D)) {
FD->setBody(nullptr);
- else if (auto *VD = dyn_cast<VarDecl>(&D))
+ // Reset the body state so a later PTU sees this as not instantiated.
+ FD->setWillHaveBody(false);
+
+ // The reverted body may have caused a substitution failure to mark
+ // the function invalid. Clear that state as well.
+ FD->setInvalidDecl(false);
+ } else if (auto *VD = dyn_cast<VarDecl>(D)) {
VD->setInit(nullptr);
- else if (auto *Field = dyn_cast<FieldDecl>(&D))
+ VD->setInvalidDecl(false);
+ } else if (auto *Field = dyn_cast<FieldDecl>(D))
Field->setInClassInitializer(nullptr);
+ else if (auto *ED = dyn_cast<EnumDecl>(D)) {
+ // Undo EnumDecl::completeDefinition() writes. PromotionType and both
+ // bit-count fields are always overwritten during completion, so they can
+ // be reset unconditionally.
+ ED->setPromotionType(QualType());
+ // resetEnumBits(*ED);
+ // EnumDecl::setNumPositiveBits/setNumNegativeBits -- private.
+ // ED->setNumPositiveBits(0);
+ // ED->setNumNegativeBits(0);
+
+ // completeDefinition() only sets IntegerType for non-fixed enums. A fixed
+ // enum may already have an IntegerType from its forward declaration, which
+ // must be preserved.
+ if (!ED->isFixed())
+ ED->setIntegerType(QualType());
+ ED->setCompleteDefinition(false);
+ clearBeingDefined(*ED);
+ } else if (auto *RD = dyn_cast<CXXRecordDecl>(D)) {
+ // 1. DefinitionData pointer -- back to null.
+ RD->DefinitionData = nullptr;
+
+ // 2. DeclContext member list -- back to empty.
+ clearDeclContextChain(*RD);
+ DeclContext *Primary = RD->getDeclContext()->getPrimaryContext();
+ if (StoredDeclsMap *Map = Primary->getLookupPtr())
+ Map->clear();
+
+ // 3. TagDecl completion bits -- back to "never started". Flipped by
+ // startDefinition()/completeDefinition().
+ clearBeingDefined(*RD);
+ RD->setCompleteDefinition(false);
+ }
}
void DeclStateReverter::removeSpecializations(
@@ -276,19 +299,18 @@ static DeclShape classifyShape(const Decl *D) {
return DeclShape::None;
}
-/// Called once when a decl is first seen. This only sets up the possible
-/// MutationRecord kinds; it does not detect the mutation itself.
-///
-/// Add a kind here only if we need to do something before the mutation:
-/// - save the old value because it will be overwritten before the listener
-/// runs, or
-/// - start hidden tracking because there is no listener for that change.
-///
-/// If the listener itself is enough to detect the change, don't add it here.
-/// noteMutated() will create the record when the change actually happens.
+/// This is called when a decl is created or seen for the first time. It also
+/// checks whether the decl is still in a mutation-sensitive state, meaning that
+/// a later PTU can modify it. Because of this, we need to track its state. This
+/// function provides a single place to decide whether a decl needs to be
+/// committed or not. It is called from DeclStateCommitPolicy::process to
+/// determine whether a new decl needs to be tracked.
///
-/// Explicit specializations are terminal, so their SpecInfo/MemberSpecInfo
-/// does not need to be tracked.
+/// This requires DeclShape S and Decl to determine which mutation types
+/// apply to the shape and whether they need to be tracked.
+/// In the future, if a new mutation site is found for a shape, it should be
+/// added here along with the logic to determine whether the decl is still in an
+/// open, modifiable state.
uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
switch (S) {
case DeclShape::Class: {
@@ -296,6 +318,8 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
uint32_t Kinds = 0;
// TypeForDecl is written twice for TagDecls. Keep tracking until both
// writes are done.
+ // Requires friend access to TypeDecl and was added for
+ // memory restoration, so we can keep it commented out for now.
// if (const Type *T = DeclStateReverter::getRawTypeForDecl(RD);
// !T || T->isCanonicalUnqualified())
// Kinds |= uint32_t(MutationType::TypeForDecl);
@@ -313,6 +337,12 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
Kinds |= uint32_t(MutationType::DefinitionData);
}
if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+ // If SpecializationKind == TSK_ExplicitSpecialization, there is less
+ // chance that the info will be modified later, so we are all set and
+ // don't need to track it.
+ // A specialization's Kind/point-of-instantiation is overwritten in place,
+ // with no separate record of the previous value. The footprint chain is
+ // the only place where the previous value is stored.
if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization)
Kinds |= uint32_t(MutationType::SpecInfo);
} else if (const auto *MSI = RD->getMemberSpecializationInfo()) {
@@ -325,7 +355,7 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
const auto *FD = cast<FunctionDecl>(D);
uint32_t Kinds = 0;
const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
- // Exception-spec resolution changes the type before the listener runs,
+ // Exception-spec resolution changes the type before the listener notify,
// so we need the old type saved beforehand.
if (FPT && (FPT->getExceptionSpecType() == EST_Unevaluated ||
FPT->getExceptionSpecType() == EST_Uninstantiated ||
@@ -333,14 +363,11 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
FPT->getExceptionSpecType() == EST_Unparsed))
Kinds |= uint32_t(MutationType::ExceptionSpec);
if (FD->getReturnType()->isUndeducedType())
- // Deduced return type works the same way: the type is changed before
- // the listener runs, so save it beforehand.
+ // Deduced return type is changed before
+ // the listener notify, so save it beforehand.
Kinds |= uint32_t(MutationType::DeducedReturnType);
if (FD->getTemplateSpecializationInfo() &&
FD->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
- // A specialization’s Kind/point-of-instantiation is overwritten in place,
- // with no separate record of the old value. The footprint chain is the
- // only place where the previous value is preserved.
Kinds |= uint32_t(MutationType::SpecInfo);
if (const auto *MSI = FD->getMemberSpecializationInfo()) {
if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
@@ -353,7 +380,6 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
const auto *VD = cast<VarDecl>(D);
uint32_t Kinds = 0;
if (const auto *Spec = dyn_cast<VarTemplateSpecializationDecl>(VD)) {
- // Same terminal-state exclusion as the Class/Function cases above.
if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization)
Kinds |= uint32_t(MutationType::SpecInfo);
} else if (const auto *MSI = VD->getMemberSpecializationInfo()) {
@@ -364,11 +390,7 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
// constant-evaluated, which can happen in any PTU long after the VarDecl
// was created. There is no ASTMutationListener event for this
// (VarDecl::evaluateValueImpl doesn’t notify anything), so this is a
- // hidden kind that we have to check live here, just like
- // Shape::Template’s CommonCreated.
- //
- // getEvaluatedStmt() is a pure read – unlike evaluateValue(), it doesn’t
- // trigger evaluation itself, so it’s safe to check on every classify call.
+ // hidden kind that we have to check here.
if (const EvaluatedStmt *Eval = VD->getEvaluatedStmt();
!Eval || !Eval->WasEvaluated)
Kinds |= uint32_t(MutationType::EvaluatedValue);
@@ -379,16 +401,12 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
const auto *ED = cast<EnumDecl>(D);
uint32_t Kinds = 0;
// TypeForDecl: EnumDecl is also a TagDecl, so the same two-write pattern
- // and “still open” condition as the Class case above apply here – see
- // its comment for the details.
- //
+ // and "still open" condition as in the Class case above apply here. See
+ // its comment for details.
// if (const Type *T = DeclStateReverter::getRawTypeForDecl(ED);
// !T || T->isCanonicalUnqualified())
// Kinds |= uint32_t(MutationType::TypeForDecl);
//
- // MSI-backed part: for an ordinary enum, its own completion is the
- // redeclaration-creates-a-new-object case. EnumDecl is Redeclarable just
- // like CXXRecordDecl, so the same reasoning as Function/Var applies here.
if (const auto *MSI = ED->getMemberSpecializationInfo()) {
// Same terminal-state exclusion as the other MSI-backed cases above.
if (MSI->getTemplateSpecializationKind() != TSK_ExplicitSpecialization)
@@ -419,7 +437,7 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
}
case DeclShape::Typedef: {
// TypedefDecl/TypeAliasDecl only -- ASTContext::getTypedefType() has its
- // own guard (if (Decl->TypeForDecl) // return), so this is effectively
+ // own guard (if (Decl->TypeForDecl) // return), so this is
// write-once: once non-null, TypeForDecl
// never changes again for this decl.
// const auto *TD = cast<TypedefNameDecl>(D);
@@ -448,13 +466,23 @@ static FootprintT Footprint(const OwnerT &Owner) {
return FP;
}
-/// The single place to check whether a kind actually changed.
+/// The single place to check whether a kind has actually changed.
+///
+/// This handles both listener-less kinds and listener-unconfirmed mutations
+/// (currently SpecInfo and MemberSpecInfo). Mutations can be detected by
+/// both SweepTracker and the listener re-check. SweepTracker tracks hidden
+/// and listener-unconfirmed mutations in case the listener misses them. If the
+/// listener catches a mutation, it is removed from SweepTracker; see
+/// PTUStateInfo::verifyMutations. both use this functionality to verify
+/// mutation kind.
///
-/// It handles both listener-less kinds and SpecInfo/MemberSpecInfo. The
-/// latter can be checked from both the sweep and the listener re-check.
+/// This check uses the decl shape and mutation kind to determine whether the
+/// decl has changed from its previous state.
///
-/// Keeping everything in one function makes sure SpecInfo/MemberSpecInfo
-/// changes are caught even when the listener never fires.
+/// Similarly, if a new hidden or listener-unconfirmed mutation is introduced
+/// in DeclNeedingTracking, and we need to determine whether it changed the
+/// previous state, the corresponding logic should be added here too
+/// when the kind is categorized as a hidden mutation from the listener.
uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
uint32_t FlaggedKinds) {
uint32_t Verified = 0;
@@ -462,7 +490,9 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
case DeclShape::Class: {
const auto *RD = cast<CXXRecordDecl>(D);
if (FlaggedKinds & uint32_t(MutationType::TypeForDecl)) {
- // No listener at all for this mutation.
+ // No listener at all for this mutation. why it is commented out:
+ // Requires friend access to TypeDecl and was added for
+ // memory restoration, so we can keep it commented out for now.
// const Type *Now = DeclStateReverter::getRawTypeForDecl(RD);
// if (differsFromLast(Tracker, RD, MutationType::TypeForDecl, Now))
// Verified |= uint32_t(MutationType::TypeForDecl);
@@ -490,7 +520,7 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
Footprint<MemberSpecializationFootprint>(*FD)))
Verified |= uint32_t(MutationType::MemberSpecInfo);
}
- break; // Function carries no listener-less kind at all.
+ break;
}
case DeclShape::Var: {
const auto *VD = cast<VarDecl>(D);
@@ -529,7 +559,7 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
break;
}
case DeclShape::Template: {
- // Use the canonical key. commitTemplate() tracks and settles CommonBase
+ // Use the canonical key. commitTemplate() tracks and untrack CommonBase
// modifications, and CommonPtr is shared across the redeclaration chain.
// Any modification to CommonPtr is reflected in the canonical declaration.
const auto *RT = cast<RedeclarableTemplateDecl>(D)->getCanonicalDecl();
@@ -554,7 +584,6 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
if (FlaggedKinds & uint32_t(MutationType::TypeForDecl) &&
Tracker.getHiddenMutationTracker().isTrackedFor(
TD, uint32_t(MutationType::TypeForDecl))) {
- // No chain to compare against -- write-once field.
// if (DeclStateReverter::getRawTypeForDecl(TD))
// Verified |= uint32_t(MutationType::TypeForDecl);
}
@@ -566,6 +595,12 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
return Verified;
}
+/// A single function called during commit and restore (when the PTU is
+/// uncommitted).
+///
+/// This is where we check for any hidden mutations previously registered with
+/// SweepTracker. If a mutation is confirmed, we record it in the
+/// PTUStateInfo mutation set via OnConfirmed.
template <typename OnConfirmedFn>
void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
for (auto &Entry : Active) {
@@ -578,43 +613,54 @@ void SweepTracker::sweep(PTUMutationActions &Act, OnConfirmedFn OnConfirmed) {
}
}
-// These kinds have listeners, but the listener alone can’t reliably confirm
-// a real change. So, compare the current value with the last known value,
-// just like we do for kinds without listeners.
-//
-// These kinds are also checked by the sweep. This is needed because the
-// listener can report a change that didn’t happen, or fail to fire when a
-// real change did happen. The sweep catches the second case.
+/// These kinds have listeners, but listeners alone cannot reliably detect
+/// real changes. Compare the current value with the last known value, as we
+/// do for kinds without listeners.
+///
+/// These kinds are also checked by the sweep to catch changes that listeners
+/// miss or report incorrectly.
+///
+/// Currently, SpecInfo/MemberSpecInfo are tracked from the listener side.
+/// This applies when the listener reports that a mutation may have happened
+/// but it has not been confirmed yet.
+///
+/// Not all hidden mutations are tracked here; they are tracked in
+/// SweepTracker. This logic applies only to listener-unconfirmed mutations.
static constexpr uint32_t KindsNeedingVerification =
uint32_t(MutationType::SpecInfo) | uint32_t(MutationType::MemberSpecInfo);
-/// Start tracking D for the mutation kinds that need live verification.
+/// Start tracking D for mutation kinds that need verification.
/// Called when a new mutation is committed or an undone mutation is restored.
-/// The callers have already established which kinds need tracking, so no
-/// additional DeclNeedingTracking check is needed.
-void PTUMutationActions::registerLiveVerification(const Decl *D,
- uint32_t Kinds) {
- if (uint32_t Live = Kinds & KindsNeedingVerification)
- HiddenMutationTracker.track(D, Live);
+/// we apply this for listener unconfirmed cases.
+void PTUMutationActions::trackForSweep(const Decl *D, uint32_t Kinds) {
+ if (uint32_t Flag = Kinds & KindsNeedingVerification)
+ HiddenMutationTracker.track(D, Flag);
}
-/// Stop tracking Decl D when its mutation no longer needs live verification.
-/// Called from verifyMutations() and the !IsNew SpecInfo/MemberSpecInfo
-/// paths in commitDecl. Re-check DeclNeedingTracking to match the same
-/// "is this still open?" condition used when D is first discovered:
-/// keep tracking if a later PTU can still update it; otherwise settle it.
-void PTUMutationActions::settleIfClosed(const Decl *D, DeclShape S,
- uint32_t Confirmed) {
- uint32_t Live = Confirmed & KindsNeedingVerification;
- if (!Live)
+/// Stop tracking Decl D when it no longer needs verification.
+/// Re-check DeclNeedingTracking to see if a later PTU can still update D.
+void PTUMutationActions::untrackIfClosed(const Decl *D, DeclShape S,
+ uint32_t Confirmed) {
+ uint32_t Flag = Confirmed & KindsNeedingVerification;
+ if (!Flag || !HiddenMutationTracker.isTrackedFor(D, Flag))
return;
- uint32_t StillOpen = DeclNeedingTracking(S, D) & Live;
- uint32_t Closed = Live & ~StillOpen;
- if (Closed && HiddenMutationTracker.isTrackedFor(D, Closed))
- HiddenMutationTracker.settle(D, Closed);
+ uint32_t StillOpen = DeclNeedingTracking(S, D) & Flag;
+ uint32_t Closed = Flag & ~StillOpen;
+ if (Closed)
+ HiddenMutationTracker.untrack(D, Closed);
+}
+
+// Shared sweep-tracking logic(track/untrack) for
+// commitClass/commitFunction/commitVar/commitEnum.
+void PTUMutationActions::syncSweepTracking(const Decl *D, DeclShape S,
+ uint32_t Kinds, bool IsNew) {
+ if (IsNew)
+ trackForSweep(D, Kinds);
+ else
+ untrackIfClosed(D, S, Kinds);
}
-/// Verify recorded mutations and remove any spurious ones. If none of a
+/// Verify recorded mutations and remove any false mutation ones. If none of a
/// decl’s mutation kinds are confirmed, remove the record entirely so
/// restore() does not pop chain entries that were never written.
void PTUStateInfo::verifyMutations(PTUMutationActions &Actions) {
@@ -623,9 +669,10 @@ void PTUStateInfo::verifyMutations(PTUMutationActions &Actions) {
MutationRecord &Rec = Entry.second;
uint32_t Flagged = Rec.MutationType & KindsNeedingVerification;
if (!Flagged)
- return false; // nothing here needs live verification.
+ return false;
uint32_t Confirmed = Actions.confirmMutation(D, Rec.S, Flagged);
- Actions.settleIfClosed(D, Rec.S, Confirmed);
+ /// untrack kind from SweepTracker if confirmed.
+ Actions.untrackIfClosed(D, Rec.S, Confirmed);
Rec.MutationType = (Rec.MutationType & ~Flagged) | Confirmed;
return Rec.MutationType == 0;
});
@@ -655,13 +702,13 @@ class DeclStateCommitPolicy {
if (S == DeclShape::None)
return;
- // we have to handle every case that if a decl need to create baseline
- // info or if any decl has done all mutations already in this for exmpla
- // membe spec already properly done like this kind of case so we don't
- // create spec info blindly
+ // Handle cases where baseline info needs to be created, as well as cases
+ // where a declaration has already completed all required mutations. For
+ // example, member specs may already be fully populated, so we should avoid
+ // creating spec info blindly.
uint32_t Kinds = Action.DeclNeedingTracking(S, D);
if (!Kinds)
- return; // structurally immutable -- no baseline needed
+ return;
MutationRecord Rec{S, Kinds};
Action.commitDecl(ID, D, Rec, /*IsNew=*/true);
@@ -706,9 +753,9 @@ class DeclStateUnlinkPolicy {
return Def && Tracker.isFromThisPTU(Def, ID);
};
- /// Keyed by the canonical declaration: all redeclarations share a single
- /// RedeclLink, so truncating more than once would advance past the intended
- /// surviving declaration.
+ /// Use the canonical decl because redecl unlinking should happen once per
+ /// redecl chain, not once per individual decl. A PTU can contain multiple
+ /// redecls, so we perform the unlinking only once.
bool DetachFromRedecl = !RepairedChains.count(Canon) &&
D->getPreviousDecl() &&
!Tracker.isFromThisPTU(Canon, ID);
@@ -716,8 +763,6 @@ class DeclStateUnlinkPolicy {
if (needsDefDataDetach(D))
Reverter.detachDefData(D);
- // Keyed on the primary context, a different granularity: a reopened
- // namespace's redeclarations all resolve to the same primary.
if (const DeclContext *DC = D->getDeclContext()->getPrimaryContext();
TouchedDC.contains(DC))
Reverter.detachFromDCLookup(const_cast<Decl *>(D), ID);
@@ -736,7 +781,8 @@ class DeclStateUnlinkPolicy {
}
if (const DeclContext *LexicalDC = D->getLexicalDeclContext();
- D->isImplicit() && !RepairedLexicalContexts.count(LexicalDC)) {
+ !Tracker.isFromThisPTU(LexicalDC, ID) &&
+ !RepairedLexicalContexts.count(LexicalDC)) {
Reverter.repairLexicalChain(*const_cast<DeclContext *>(LexicalDC), ID);
RepairedLexicalContexts.insert(LexicalDC);
}
@@ -757,6 +803,22 @@ void PTUMutationActions::walkDecls(const DeclContext *DC,
//////////////////////// PTUMutationActions::commit ///////////////////////
/// -----------------------------------------------------------------------
+/// This are the places to commit or update the incremental state of a decl
+/// based on its DeclShape.
+///
+/// The idea is to narrow each decl shape down to a small set of known
+/// mutation sites that require their state to be registered with
+/// IncrementalStateTracker. The state is committed when the decl is newly
+/// created by this PTU, and updated when an existing decl is mutated.
+///
+/// Currently, known mutation sites include commitClass, commitFunction,
+/// commitEnum, commitTemplate, etc. This follows a DeclShape ->
+/// mutation type relationship.
+///
+/// When adding a new mutation site that requires committing or updating
+/// state, it should be added based on the corresponding DeclShape ->
+/// mutation type relationship.
+
void PTUMutationActions::commitMembers(PTUID ID, const DeclContext *Members) {
DeclStateCommitPolicy Policy(ID, *this);
walkDecls(Members, Policy);
@@ -768,6 +830,7 @@ void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
using ClassMutation = MutationType;
if (Rec.has(ClassMutation::TypeForDecl)) {
+ // Require friend grant to TypeDecl.
// for (const TagDecl *Redecl : RD->redecls()) {
// const auto *Last = Tracker.getFootprints().mostRecent<const Type *>(
// Redecl, MutationType::TypeForDecl);
@@ -784,7 +847,6 @@ void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
RD, MutationType::DefinitionData, ID,
DeclStateReverter::createDefinitionDataFootprint(*RD));
- /// SpecializationDecl can have lazy implicit generated body;
if (Rec.has(ClassMutation::DefinitionInstantiate)) {
if (RD->getMemberSpecializationInfo())
commitMembers(ID, cast<DeclContext>(RD));
@@ -797,33 +859,29 @@ void PTUMutationActions::commitClass(PTUID ID, const CXXRecordDecl *RD,
Tracker.commitFootprint(
Spec, MutationType::SpecInfo, ID,
DeclStateReverter::createFootprint<SpecializationFootprint>(*Spec));
- // Member declarations are instantiated eagerly with the class
- // definition regardless of TSK; only their definitions defer. Walk
- // them now so each member's MemberSpecializationInfo is tracked from
- // the moment it exists.
+ /// Member declarations are instantiated eagerly with the class
+ /// definition, egardless of TSK; only their definitions are deferred.
+ ///
+ /// Walk them here so each member’s MemberSpecializationInfo is tracked as
+ /// soon as it is created.
if (IsNew && Spec->getSpecializationKind() == TSK_ImplicitInstantiation)
commitMembers(ID, cast<DeclContext>(Spec));
- if (IsNew)
- registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
- else
- settleIfClosed(Spec, DeclShape::Class,
- uint32_t(MutationType::SpecInfo));
+ // FIXME: Remove syncSweepTracking from all places
+ // (SpecInfo/MemberSpecInfo) once the missing notifier
+ // (ASTMutationListener) is added for (SpecInfo/MemberSpecInfo).
+ syncSweepTracking(Spec, DeclShape::Class,
+ uint32_t(MutationType::SpecInfo), IsNew);
}
}
if (Rec.has(ClassMutation::MemberSpecInfo)) {
- // A nested member of a class template -- its MSI's kind or
- // point-of-instantiation changed.
if (RD->getMemberSpecializationInfo()) {
Tracker.commitFootprint(
RD, MutationType::MemberSpecInfo, ID,
DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
*RD));
- if (IsNew)
- registerLiveVerification(RD, uint32_t(MutationType::MemberSpecInfo));
- else
- settleIfClosed(RD, DeclShape::Class,
- uint32_t(MutationType::MemberSpecInfo));
+ syncSweepTracking(RD, DeclShape::Class,
+ uint32_t(MutationType::MemberSpecInfo), IsNew);
}
}
}
@@ -858,10 +916,8 @@ void PTUMutationActions::commitFunction(PTUID ID, const FunctionDecl *FD,
FD, MutationType::SpecInfo, ID,
DeclStateReverter::createFootprint<FunctionSpecializationFootprint>(
*FD));
- if (IsNew)
- registerLiveVerification(FD, uint32_t(MutationType::SpecInfo));
- else
- settleIfClosed(FD, DeclShape::Function, uint32_t(MutationType::SpecInfo));
+ syncSweepTracking(FD, DeclShape::Function, uint32_t(MutationType::SpecInfo),
+ IsNew);
}
if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -870,11 +926,8 @@ void PTUMutationActions::commitFunction(PTUID ID, const FunctionDecl *FD,
FD, MutationType::MemberSpecInfo, ID,
DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
*FD));
- if (IsNew)
- registerLiveVerification(FD, uint32_t(MutationType::MemberSpecInfo));
- else
- settleIfClosed(FD, DeclShape::Function,
- uint32_t(MutationType::MemberSpecInfo));
+ syncSweepTracking(FD, DeclShape::Function,
+ uint32_t(MutationType::MemberSpecInfo), IsNew);
}
}
}
@@ -882,28 +935,20 @@ void PTUMutationActions::commitFunction(PTUID ID, const FunctionDecl *FD,
void PTUMutationActions::commitVar(PTUID ID, const VarDecl *VD,
MutationRecord &Rec, bool IsNew) {
- // EvaluatedStmt / APValue: populated on the FIRST constant-evaluation of
- // this variable, which can land in any PTU long after the VarDecl was
- // created. This is a mutation of an earlier PTU's decl, not a property
- // settled at declaration time.
+ // EvaluatedStmt / APValue: set on the first constant evaluation of the
+ // variable, which can happen in a later PTU.
if (Rec.has(MutationType::EvaluatedValue)) {
- // No footprint chain -- write-once field with a fixed null prior
- // state (see restoreVar), so nothing to seed a baseline for.
+ // No footprint: this is a write-once field with a fixed null
+ // initial state, so there is no baseline to track.
if (IsNew) {
- // Just created: register VD so a later commit's
- // SweepTracker::sweep() call notices if/when WasEvaluated actually
- // flips (no listener exists to tell us directly -- see
- // hiddenKindsMask).
+ // Track the VarDecl so a later sweep can detect when WasEvaluated flips.
+ // There is no listener for this mutation.
HiddenMutationTracker.track(VD, uint32_t(MutationType::EvaluatedValue));
} else {
- // This IS the confirmation, arriving here via SweepTracker::sweep()'s
- // OnConfirmed callback (there is no other path to this bit -- no
- // listener exists for it). Write-once means terminal: once
- // WasEvaluated is true it can never become false again on its own,
- // so settle the bit now rather than waiting for the next sweep to
- // notice kindsNeedingTracking no longer flags it. restoreVar
- // is what re-track()s it, only if a rollback actually reverts this.
- HiddenMutationTracker.settle(VD, uint32_t(MutationType::EvaluatedValue));
+ // This is the confirmation from SweepTracker::sweep(). Since this is a
+ // write-once field, it is now settled and no longer needs tracking.
+ // restoreVar() will re-track it if a rollback reverts the mutation.
+ HiddenMutationTracker.untrack(VD, uint32_t(MutationType::EvaluatedValue));
}
}
@@ -911,15 +956,12 @@ void PTUMutationActions::commitVar(PTUID ID, const VarDecl *VD,
// }
if (Rec.has(MutationType::SpecInfo)) {
- // Unlike functions, a variable specialization IS a distinct type.
const auto *Spec = cast<VarTemplateSpecializationDecl>(VD);
Tracker.commitFootprint(
Spec, MutationType::SpecInfo, ID,
DeclStateReverter::createFootprint<VarSpecializationFootprint>(*Spec));
- if (IsNew)
- registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
- else
- settleIfClosed(Spec, DeclShape::Var, uint32_t(MutationType::SpecInfo));
+ syncSweepTracking(Spec, DeclShape::Var, uint32_t(MutationType::SpecInfo),
+ IsNew);
}
if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -928,11 +970,8 @@ void PTUMutationActions::commitVar(PTUID ID, const VarDecl *VD,
VD, MutationType::MemberSpecInfo, ID,
DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
*VD));
- if (IsNew)
- registerLiveVerification(VD, uint32_t(MutationType::MemberSpecInfo));
- else
- settleIfClosed(VD, DeclShape::Var,
- uint32_t(MutationType::MemberSpecInfo));
+ syncSweepTracking(VD, DeclShape::Var,
+ uint32_t(MutationType::MemberSpecInfo), IsNew);
}
}
}
@@ -956,18 +995,13 @@ void PTUMutationActions::commitEnum(PTUID ID, const EnumDecl *ED,
}
if (Rec.has(MutationType::MemberSpecInfo)) {
- // A scoped member enumeration of a class template -- instantiated with
- // the enclosing specialization, carrying MSI back to the pattern enum.
if (ED->getMemberSpecializationInfo()) {
Tracker.commitFootprint(
ED, MutationType::MemberSpecInfo, ID,
DeclStateReverter::createFootprint<MemberSpecializationFootprint>(
*ED));
- if (IsNew)
- registerLiveVerification(ED, uint32_t(MutationType::MemberSpecInfo));
- else
- settleIfClosed(ED, DeclShape::Enum,
- uint32_t(MutationType::MemberSpecInfo));
+ syncSweepTracking(ED, DeclShape::Enum,
+ uint32_t(MutationType::MemberSpecInfo), IsNew);
}
}
}
@@ -981,8 +1015,8 @@ void PTUMutationActions::commitTemplate(PTUID ID,
HiddenMutationTracker.track(RTCanon,
uint32_t(MutationType::TemplateCommon));
else
- HiddenMutationTracker.settle(RTCanon,
- uint32_t(MutationType::TemplateCommon));
+ HiddenMutationTracker.untrack(RTCanon,
+ uint32_t(MutationType::TemplateCommon));
}
if (Rec.has(MutationType::CanonInjectedTST)) {
@@ -990,8 +1024,8 @@ void PTUMutationActions::commitTemplate(PTUID ID,
HiddenMutationTracker.track(RTCanon,
uint32_t(MutationType::CanonInjectedTST));
else
- HiddenMutationTracker.settle(RTCanon,
- uint32_t(MutationType::CanonInjectedTST));
+ HiddenMutationTracker.untrack(RTCanon,
+ uint32_t(MutationType::CanonInjectedTST));
}
}
@@ -1001,7 +1035,7 @@ void PTUMutationActions::commitTypedef(PTUID ID, const TypedefNameDecl *TD,
if (IsNew)
HiddenMutationTracker.track(TD, uint32_t(MutationType::TypeForDecl));
else
- HiddenMutationTracker.settle(TD, uint32_t(MutationType::TypeForDecl));
+ HiddenMutationTracker.untrack(TD, uint32_t(MutationType::TypeForDecl));
}
}
@@ -1024,20 +1058,34 @@ void PTUMutationActions::commit(TranslationUnitDecl *ThisTU) {
assert(!Cur.Commited);
PTUID ID = Cur.ID;
+ // 1. Verify any unconfirmed mutation (see : KindNeedingVerification) reported
+ // by the listener, whether true or false.
Cur.verifyMutations(*this);
+ // 2. Catch any mutation-sensitive state of Decl from previous PTUs that
+ // ASTMutationListener cannot catch if it was registered during a
+ // previous PTU commit.
Tracker.getHiddenMutationTracker().sweep(
*this, [&](const Decl *D, DeclShape S, uint32_t K) {
Cur.noteMutated(D, S, K);
});
+ // 3. Update the tracking state of declarations from the previous PTU using
+ // Cur.Mutations reported by ASTMutationListener and HiddenMutationTracker.
for (auto &[D, Rec] : Cur.Mutations)
commitDecl(ID, D, Rec, /*IsNew=*/false);
- DeclStateCommitPolicy Proxy(ID, *this);
+ // 4. Cur.ImplicitDecls: Declarations created by this PTU. Register them for
+ // tracking if they have mutation-sensitive state that can be modified by
+ // later PTUs.
+ //
+ // DeclStateCommitPolicy: Decides what to do with declarations created by the
+ // current PTU, such as whether a declaration needs to be tracked or not.
+ DeclStateCommitPolicy Policy(ID, *this);
for (const Decl *D : Cur.ImplicitDecls)
- Proxy.process(D);
+ Policy.process(D);
- walkDecls(ThisTU, Proxy);
+ // Iterate over all declarations from this TU and apply DeclStateCommitPolicy.
+ walkDecls(ThisTU, Policy);
Cur.Commited = true;
}
@@ -1046,6 +1094,18 @@ void PTUMutationActions::commit(TranslationUnitDecl *ThisTU) {
//////////////////////// PTUMutationActions::restore //////////////////////
/// -----------------------------------------------------------------------
+/// These are the places where we restore or undo/unlink the incremental state
+/// of a decl based on its DeclShape.
+///
+/// The same DeclShape -> mutation type relationship used during commit is
+/// followed here, but in the opposite direction. Restore handles unlinking
+/// newly created decls and undoing the committed or updated state of existing
+/// decls, restoring them to their previous state.
+///
+/// As with commit, when adding a new mutation site that requires restoring or
+/// unlinking state, it should be added based on the corresponding
+/// DeclShape -> mutation type relationship.
+
void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
MutationRecord &Rec) {
@@ -1053,7 +1113,7 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
Rec.has(MutationType::DefinitionData)) {
if (Rec.has(MutationType::DefinitionInstantiate)) {
DeclStateReverter::revertDefinitionArrival(
- *const_cast<CXXRecordDecl *>(RD));
+ const_cast<CXXRecordDecl *>(RD));
} else if (const auto *Restored =
Tracker.getFootprints().mostRecent<DefinitionDataFootprint>(
RD, MutationType::DefinitionData)) {
@@ -1062,7 +1122,6 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
}
}
- // TypeForDecl is shared across the whole redeclaration chain -- revert all.
if (Rec.has(MutationType::TypeForDecl)) {
// for (const TagDecl *Redecl : RD->redecls()) {
// const auto *Last = Tracker.Footprints.mostRecent<const Type *>(
@@ -1082,7 +1141,7 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
Spec, MutationType::SpecInfo))
DeclStateReverter::restoreFootprint<SpecializationFootprint>(
*Restored, *const_cast<ClassTemplateSpecializationDecl *>(Spec));
- registerLiveVerification(Spec, uint32_t(MutationType::SpecInfo));
+ trackForSweep(Spec, uint32_t(MutationType::SpecInfo));
}
if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -1094,7 +1153,7 @@ void PTUMutationActions::restoreClass(PTUID ID, const CXXRecordDecl *RD,
RD, MutationType::MemberSpecInfo))
DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
*Restored, *const_cast<CXXRecordDecl *>(RD));
- registerLiveVerification(RD, uint32_t(MutationType::MemberSpecInfo));
+ trackForSweep(RD, uint32_t(MutationType::MemberSpecInfo));
}
}
@@ -1117,7 +1176,7 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
}
if (Rec.has(MutationType::DefinitionInstantiate))
- DeclStateReverter::revertDefinitionArrival(*const_cast<FunctionDecl *>(FD));
+ DeclStateReverter::revertDefinitionArrival(const_cast<FunctionDecl *>(FD));
if (Rec.has(MutationType::SpecInfo)) {
// A function template specialization is a plain FunctionDecl carrying
@@ -1129,7 +1188,8 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
FD, MutationType::SpecInfo))
DeclStateReverter::restoreFootprint<FunctionSpecializationFootprint>(
*Restored, *const_cast<FunctionDecl *>(FD));
- registerLiveVerification(FD, uint32_t(MutationType::SpecInfo));
+ const_cast<FunctionDecl *>(FD)->setInstantiationIsPending(false);
+ trackForSweep(FD, uint32_t(MutationType::SpecInfo));
}
if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -1139,7 +1199,8 @@ void PTUMutationActions::restoreFunction(PTUID ID, const FunctionDecl *FD,
FD, MutationType::MemberSpecInfo))
DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
*Restored, *const_cast<FunctionDecl *>(FD));
- registerLiveVerification(FD, uint32_t(MutationType::MemberSpecInfo));
+ const_cast<FunctionDecl *>(FD)->setInstantiationIsPending(false);
+ trackForSweep(FD, uint32_t(MutationType::MemberSpecInfo));
}
}
@@ -1177,9 +1238,6 @@ void PTUMutationActions::restoreTemplate(PTUID ID,
void PTUMutationActions::restoreTypedef(PTUID ID, const TypedefNameDecl *TD,
MutationRecord &Rec) {
if (Rec.has(MutationType::TypeForDecl)) {
- // TD survives this rollback (only the cached Type was this PTU's
- // doing) -- reset the live field and re-register: some later PTU
- // could re-trigger ASTContext::getTypedefType's first-call write.
// DeclStateReverter::resetTypeForDecl(const_cast<TypedefNameDecl *>(TD));
// HiddenMutationTracker.track(TD, uint32_t(MutationType::TypeForDecl));
}
@@ -1200,7 +1258,7 @@ void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
}
if (Rec.has(MutationType::DefinitionInstantiate)) {
- DeclStateReverter::revertDefinitionArrival(*const_cast<VarDecl *>(VD));
+ DeclStateReverter::revertDefinitionArrival(const_cast<VarDecl *>(VD));
}
if (Rec.has(MutationType::SpecInfo)) {
@@ -1210,7 +1268,7 @@ void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
Spec, MutationType::SpecInfo))
DeclStateReverter::restoreFootprint<VarSpecializationFootprint>(
*Restored, *const_cast<VarTemplateSpecializationDecl *>(Spec));
- registerLiveVerification(VD, uint32_t(MutationType::SpecInfo));
+ trackForSweep(VD, uint32_t(MutationType::SpecInfo));
}
if (Rec.has(MutationType::MemberSpecInfo)) {
@@ -1220,7 +1278,7 @@ void PTUMutationActions::restoreVar(PTUID ID, const VarDecl *VD,
VD, MutationType::MemberSpecInfo))
DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
*Restored, *const_cast<VarDecl *>(VD));
- registerLiveVerification(VD, uint32_t(MutationType::MemberSpecInfo));
+ trackForSweep(VD, uint32_t(MutationType::MemberSpecInfo));
}
}
@@ -1236,9 +1294,8 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
// }
}
- // if (Rec.has(MutationType::DefinitionInstantiate))
- // DeclStateReverter::revertDefinitionArrival(*const_cast<EnumDecl
- // *>(ED));
+ if (Rec.has(MutationType::DefinitionInstantiate))
+ DeclStateReverter::revertDefinitionArrival(const_cast<EnumDecl *>(ED));
if (Rec.has(MutationType::MemberSpecInfo)) {
assert(ED->getMemberSpecializationInfo());
@@ -1248,7 +1305,7 @@ void PTUMutationActions::restoreEnum(PTUID ID, const EnumDecl *ED,
DeclStateReverter::restoreFootprint<MemberSpecializationFootprint>(
*Restored, *const_cast<EnumDecl *>(ED));
- registerLiveVerification(ED, uint32_t(MutationType::MemberSpecInfo));
+ trackForSweep(ED, uint32_t(MutationType::MemberSpecInfo));
}
}
@@ -1273,13 +1330,13 @@ void PTUMutationActions::restoreSpecialMemberCache(PTUID ID) {
return;
Sema &SemaRef = Tracker.getSema();
- llvm::SmallVector<Sema::SpecialMemberCacheKey, 8> Stale;
+ llvm::SmallVector<Sema::SpecialMemberCacheKey, 8> ToRemove;
for (auto &Entry : SemaRef.SpecialMemberCache) {
CXXMethodDecl *MD = Entry.second.getMethod();
if (MD && (Tracker.isFromThisPTU(MD, ID) || Cur.ImplicitDecls.contains(MD)))
- Stale.push_back(Entry.first);
+ ToRemove.push_back(Entry.first);
}
- for (const auto &Key : Stale)
+ for (const auto &Key : ToRemove)
SemaRef.SpecialMemberCache.erase(Key);
}
@@ -1288,10 +1345,27 @@ void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
assert((!Cur.Commited || ThisTU == Cur.ThisPTU) &&
"MostRecentTU doesn't match the TU recorded at commit time");
PTUID ID = Cur.ID;
- if (Cur.Commited)
+ // 1. Check whether this PTU was committed. If the PTU failed, it was not
+ // committed and therefore did not go through the commit policy or register
+ // any declaration state, so there is nothing to undo from
+ // IncrementalStateTracker.
+ //
+ // If the PTU was successfully committed, the commit policy must have
+ // registered the declaration state and updated the state of declarations
+ // from the previous PTU in IncrementalStateTracker. First undo all state
+ // recorded by IncrementalStateTracker, then perform the regular undo so
+ // that mutated declarations are restored to the PTU(N-1) state rather
+ // than the current PTU state.
+ if (Cur.Commited) {
+ // remove sweep-tracked decls owned by this PTU,
+ // which would otherwise become stale references with no cleanup path.
+ Tracker.getHiddenMutationTracker().untrackIf(
+ [&](const Decl *D) { return Tracker.isFromThisPTU(D, ID); });
Tracker.undoLastEntries();
- else {
- // TODO: verify Mutations;
+ } else {
+ // Same as the commit() step, but performed here because this PTU failed and
+ // was never committed. Since the commit step was never executed, we need to
+ // perform it here for an uncommitted PTU.
Cur.verifyMutations(*this);
Tracker.getHiddenMutationTracker().sweep(
*this, [&](const Decl *D, DeclShape S, uint32_t K) {
@@ -1299,29 +1373,37 @@ void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
});
}
+ // 2. Same as the commit() step, but in reverse: commit() updates the
+ // IncrementalStateTracker with the mutated state of declarations from the
+ // previous state. Here, we restore each mutated declaration using its most
+ // recent state in IncrementalStateTracker (i.e., the PTU(N-1) state).
for (auto &[D, Rec] : Cur.Mutations)
restoreDecl(ID, D, Rec);
DeclStateReverter Detacher(Tracker.getPTUSlabCheckpoints());
+ // 3. Cur.ImplicitDecls: Declarations created by this PTU. Unlink them from
+ // the compiler's shared state.
+ //
+ // This is the counterpart to the commit DeclStateCommitPolicy, but
+ // performs the opposite operation by unlinking the declarations.
+ //
+ // DeclStateUnlinkPolicy: Decides what to do with declarations created by the
+ // current PTU, such as whether a declaration needs to be unlinked from
+ // redeclarations, DeclContext lookup, etc.
DeclStateUnlinkPolicy Policy(ID, Tracker, Detacher);
-
for (const Decl *D : Cur.ImplicitDecls)
Policy.process(D);
walkDecls(ThisTU, Policy);
- // SpecialMemberCache stores the CXXMethodDecl* resolved by a previous
- // LookupSpecialMember() call and returns it directly on a cache hit.
- // If that method was created by a PTU that is being rolled back, the entry
- // is stale, so remove it here.
+ // SpecialMemberCache stores the CXXMethodDecl* from a previous
+ // LookupSpecialMember() call. If it was created by a PTU being rolled back,
+ // the cache entry is stale, so remove it here.
restoreSpecialMemberCache(ID);
- // Must be last: Cur/ID/ThisTU are all references into (or derived from)
- // the PTU this call retires, and popCurrentPTU() destroys that entry.
- // Runs for BOTH branches above (committed or not) -- a PTU that never
- // committed still needs its checkpoint-ledger slot and PTUStack
- // entry reclaimed, otherwise every failed input would permanently grow
- // both, and NextID would desync from PTUSlabCheckpoints on top of that.
+ // Must be last: Cur/ID/ThisTU refer to data owned by this PTU, and
+ // popCurrentPTU() destroys that entry. This runs for both committed and
+ // rolled-back PTUs to reclaim the PTUStack and checkpoint entry.
Tracker.popCurrentPTU();
}
@@ -1329,6 +1411,33 @@ void PTUMutationActions::restore(TranslationUnitDecl *ThisTU) {
////////////////////////// MutationListener ///////////////////////////////
/// -----------------------------------------------------------------------
+/// This is the place for listener-related functionality. When Clang notifies
+/// us about an AST mutation, we record it in PTUStateInfo.
+///
+/// We only capture mutations here. For example, if a decl from a previous PTU
+/// is mutated, we record its Decl -> DeclShape -> mutation type relationship.
+/// We do not process the mutation here. Processing is deferred until commit
+/// if the PTU succeeds; if the PTU fails, the flow goes through restore
+/// instead.
+///
+/// We also track implicit decls. During commit, we walk the current PTU and
+/// can only commit decls that are visible from that TU. However, implicit decls
+/// can be created and added to another PTU, so we need to track them as well
+/// so they can be committed or unlinked during restore.
+///
+/// We do not cover every mutation case here yet. Any new case added should
+/// follow the same rule: if a mutation affects a decl from a previous PTU,
+/// record it in PTUStateInfo.
+///
+/// There are two separate restore paths:
+/// 1. Restoring a mutated decl to its previous state.
+/// 2. Unlinking a decl from shared state, such as redecl chains or DC lookup.
+///
+/// For example, see noteDefinitionInstantiated, where a decl is created by
+/// Clang and added to another PTU even though it is not marked as implicit.
+/// Such a decl needs to be tracked for unlinking rather than restoring its
+/// previous state.
+
void PTUMutationRecorder::CompletedTagDefinition(const TagDecl *D) {
if (!D->isDefinedOutsideFunctionOrMethod())
return; // ordinary local class -- structurally unreachable by any other
@@ -1400,8 +1509,9 @@ void PTUMutationRecorder::noteTemplateDeclMutation(const TemplateT *TD,
if (Spec->isImplicit())
Cur.ImplicitDecls.insert(Spec);
- if (Tracker.isFromThisPTU(TD, Cur.ID))
+ if (Tracker.isFromThisPTU(TD, Cur.ID)) {
return;
+ }
Cur.noteMutated(TD, DeclShape::Template, MutationType::SpecializationAdded);
}
@@ -1441,6 +1551,14 @@ void PTUMutationRecorder::DeducedReturnType(const FunctionDecl *FD,
void PTUMutationRecorder::noteDefinitionInstantiated(const Decl *D) {
PTUStateInfo &Cur = Tracker.current();
if (Tracker.isFromThisPTU(D, Cur.ID)) {
+ // Normally, walkDecls covers declarations created in this PTU.
+ // But an out-of-line static data member is a separate VarDecl whose
+ // lexical context is the class, so the namespace/TU walk won't find it.
+ // Process it like other implicit declarations instead.
+ const auto *VD = dyn_cast<VarDecl>(D);
+ if (VD && VD->getPreviousDecl() &&
+ !Tracker.isFromThisPTU(VD->getCanonicalDecl(), Cur.ID))
+ Cur.ImplicitDecls.insert(D);
return;
}
Cur.noteMutated(D, classifyShape(D), MutationType::DefinitionInstantiate);
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index c03881b0ed336..78fe1399f0d0e 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -571,9 +571,7 @@ Interpreter::Parse(llvm::StringRef Code) {
class PTURollbackGuard {
public:
explicit PTURollbackGuard(Sema &S)
- : Ctx(S.getASTContext()),
- CheckPoint(Ctx.getAllocator().checkPoint()) {
- }
+ : Ctx(S.getASTContext()), CheckPoint(Ctx.getAllocator().CheckPoint()) {}
~PTURollbackGuard() {
if (!Committed) {
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index 0aceaa999e2a6..56bce4d31c8a7 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -43,9 +43,9 @@ LLVM_ABI void printBumpPtrAllocatorStats(unsigned NumSlabs, size_t TotalMemory);
} // end namespace detail
struct SlabCheckPoint {
- unsigned ActiveSlabIdx;
- char *CurPtr;
- uintptr_t EndSentinel;
+ unsigned SlabIdx;
+ const char *CurPtr;
+ unsigned CustomSlabCount;
};
/// Allocate memory in an ever growing pool, as if by bump-pointer.
@@ -216,16 +216,8 @@ class BumpPtrAllocatorImpl
return AlignedPtr;
}
- if (!Slabs.empty() && Slabs.size() > 1 &&
- (ActiveSlabIdx < Slabs.size() - 1)) {
- ActiveSlabIdx++;
- void *NewSlab = Slabs[ActiveSlabIdx];
- size_t AllocatedSlabSize = computeSlabSize(ActiveSlabIdx);
- CurPtr = (char *)(NewSlab);
- EndSentinel = uintptr_t(NewSlab) + AllocatedSlabSize + 1;
- } else
- // Otherwise, start a new slab and try again.
- StartNewSlab();
+ // Otherwise, start a new slab and try again.
+ StartNewSlab();
uintptr_t AlignedAddr = alignAddr(CurPtr, Alignment);
assert(AlignedAddr + SizeToAllocate < EndSentinel &&
"Unable to allocate memory!");
@@ -257,64 +249,46 @@ class BumpPtrAllocatorImpl
size_t GetNumSlabs() const { return Slabs.size() + CustomSizedSlabs.size(); }
- SlabCheckPoint checkPoint() const {
- return {ActiveSlabIdx, CurPtr, EndSentinel};
- }
-
- static void poisonMemory(void *Ptr, size_t Size) {
-#if LLVM_ADDRESS_SANITIZER_BUILD
- __asan_poison_memory_region(Ptr, Size);
-#else
- // In non-ASAN builds, overwrite with a known poison pattern
- // so use-after-rewind crashes deterministically in debug builds
-// #ifndef NDEBUG
- memset(Ptr, 0xCD, Size); // 0xCD = classic "dead memory" pattern
-// #endif
-#endif
+ SlabCheckPoint CheckPoint() const {
+ return {static_cast<unsigned int>(Slabs.empty() ? 0u : (Slabs.size() - 1)),
+ CurPtr, static_cast<unsigned int>(CustomSizedSlabs.size())};
}
bool isAfterCheckpoint(const void *Ptr, const SlabCheckPoint &CP) const {
const char *P = static_cast<const char *>(Ptr);
- // Check active slab — past the checkpoint CurPtr
- if (CP.ActiveSlabIdx < Slabs.size()) {
- const char *Start = static_cast<const char *>(Slabs[CP.ActiveSlabIdx]);
- if (P >= CP.CurPtr && P < (Start + computeSlabSize(CP.ActiveSlabIdx)))
- return true;
+ // Regular (shared, bump-allocated) slabs.
+ if (!CP.CurPtr) {
+ // Checkpoint predates this allocator's first regular slab -- every
+ // regular slab that exists now was allocated after it.
+ for (unsigned I = 0; I < Slabs.size(); ++I) {
+ const char *Start = static_cast<const char *>(Slabs[I]);
+ if (P >= Start && P < Start + computeSlabSize(I))
+ return true;
+ }
+ } else {
+ if (CP.SlabIdx < Slabs.size()) {
+ const char *Start = static_cast<const char *>(Slabs[CP.SlabIdx]);
+ if (P >= CP.CurPtr && P < Start + computeSlabSize(CP.SlabIdx))
+ return true;
+ }
+ for (unsigned I = CP.SlabIdx + 1; I < Slabs.size(); ++I) {
+ const char *Start = static_cast<const char *>(Slabs[I]);
+ if (P >= Start && P < (Start + computeSlabSize(I)))
+ return true;
+ }
}
- // Check slabs allocated entirely after checkpoint
- for (unsigned I = CP.ActiveSlabIdx + 1; I < Slabs.size(); ++I) {
- const char *Start = static_cast<const char *>(Slabs[I]);
- const char *End = Start + computeSlabSize(I);
- if (P >= Start && P < End)
+ for (unsigned I = CP.CustomSlabCount; I < CustomSizedSlabs.size(); ++I) {
+ const char *Start = static_cast<const char *>(CustomSizedSlabs[I].first);
+ size_t Size = CustomSizedSlabs[I].second;
+ if (P >= Start && P < Start + Size)
return true;
}
return false;
}
- void restoreToCheckPoint(SlabCheckPoint CP) {
- assert(CP.ActiveSlabIdx >= 0 && CP.ActiveSlabIdx < Slabs.size());
- assert(CP.CurPtr >= (const char *)Slabs[CP.ActiveSlabIdx] &&
- CP.EndSentinel == uintptr_t(Slabs[CP.ActiveSlabIdx]) +
- computeSlabSize(CP.ActiveSlabIdx) + 1);
- ActiveSlabIdx = CP.ActiveSlabIdx;
- CurPtr = CP.CurPtr;
- EndSentinel = CP.EndSentinel;
-
- uintptr_t EndRange =
- uintptr_t(Slabs[CP.ActiveSlabIdx]) + computeSlabSize(CP.ActiveSlabIdx);
-
- llvm::outs() << "Poisoned range = [" << (void *)CurPtr << ", " << (void *)(EndSentinel - 1) << "]\n";
- llvm::outs() << "Poisoned End Size = [" << (void *)CurPtr << ", " << (void *)(CurPtr + ((EndSentinel - 1) - (uintptr_t)CurPtr)) << "]\n";
- llvm::outs().flush();
- poisonMemory((void *)CurPtr, (size_t)(EndRange - uintptr_t(CurPtr)));
- for (unsigned I = ActiveSlabIdx + 1; I < Slabs.size(); ++I)
- // Should we deallocate any extra slabs?
- poisonMemory(Slabs[I], computeSlabSize(I));
- }
-
/// \return An index uniquely and reproducibly identifying
/// an input pointer \p Ptr in the given allocator.
/// The returned value is negative iff the object is inside a custom-size
@@ -398,8 +372,6 @@ class BumpPtrAllocatorImpl
/// path condition also rejects a empty allocator with a 0-size allocation.
uintptr_t EndSentinel = 0;
- unsigned ActiveSlabIdx = 0;
-
/// The slabs allocated so far.
SmallVector<void *, 4> Slabs;
@@ -424,8 +396,7 @@ class BumpPtrAllocatorImpl
/// Allocate a new slab and move the bump pointers over into the new
/// slab, modifying CurPtr and EndSentinel.
void StartNewSlab() {
- ActiveSlabIdx = Slabs.size();
- size_t AllocatedSlabSize = computeSlabSize(ActiveSlabIdx);
+ size_t AllocatedSlabSize = computeSlabSize(Slabs.size());
void *NewSlab = this->getAllocator().Allocate(AllocatedSlabSize,
alignof(std::max_align_t));
>From 1416c7bed43bbeaa5495171a93ffb5a8aca1aba3 Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Wed, 30 Sep 2026 15:16:30 +0530
Subject: [PATCH 12/13] Some more fixes
---
.../include/clang/Interpreter/ErrorRecovery.h | 41 +++++-------
clang/lib/Interpreter/ErrRecovery.cpp | 63 +++++++------------
clang/lib/Interpreter/IncrementalParser.cpp | 4 --
3 files changed, 40 insertions(+), 68 deletions(-)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index 85f7ffef1e8a0..ee392b697af74 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -45,6 +45,14 @@ struct DefinitionDataFootprint {
bool operator!=(const DefinitionDataFootprint &O) const {
return !(*this == O);
}
+
+ // void update(const CXXRecordDecl &Live) {
+ // DeclStateReverter::createDefinitionDataFootprint(*this, Live);
+ // }
+
+ // void restore(CXXRecordDecl &Live) const {
+ // DeclStateReverter::restoreDefinitionDataFootprint(*this, Live);
+ // }
};
// Stores the state of a class template specialization. Tracks its
@@ -185,10 +193,6 @@ class DeclStateReverter {
using RedeclarableTemplateDecl::Common;
};
- static const void *rawCommonPtr(const RedeclarableTemplateDecl &RT) {
- return static_cast<const TemplateCommonAccess &>(RT).Common;
- }
-
// Common is `mutable`, so this is legal to call through a const
// reference too -- no const_cast needed at any call site.
static void clearCommonPtr(const RedeclarableTemplateDecl &RT) {
@@ -209,12 +213,6 @@ class DeclStateReverter {
using VarTemplateDecl::getSpecializations;
};
- static bool canonInjectedTSTValid(const ClassTemplateDecl &CTD) {
- auto *Ptr =
- static_cast<const ClassTemplateCommonAccess &>(CTD).getCommonPtr();
- return !Ptr->CanonInjectedTST.isNull();
- }
-
static void clearCanonInjectedTST(ClassTemplateDecl &CTD) {
auto *Ptr = static_cast<ClassTemplateCommonAccess &>(CTD).getCommonPtr();
Ptr->CanonInjectedTST = CanQualType();
@@ -266,9 +264,9 @@ class DeclStateReverter {
static void restoreDefinitionDataFootprint(const DefinitionDataFootprint &FP,
CXXRecordDecl &RD);
- // Generic across every other footprint type -- SpecializationFootprint,
- // VarSpecializationFootprint, FunctionSpecializationFootprint,
- // MemberSpecializationFootprint.
+ // Generic across every other footprint type exclusing DefinitionDataFootprint
+ // -- SpecializationFootprint, VarSpecializationFootprint,
+ // FunctionSpecializationFootprint, MemberSpecializationFootprint.
template <typename FootprintT, typename OwnerT>
static FootprintT createFootprint(const OwnerT &Owner) {
FootprintT FP;
@@ -284,18 +282,19 @@ class DeclStateReverter {
// True if Common could still be created later -- i.e. nobody has called
// getCommonPtr() anywhere in this template's redecl chain yet.
- static bool isCommonPtrValid(const RedeclarableTemplateDecl &RT) {
- return !rawCommonPtr(RT);
+ static bool isCommonPtrValid(const RedeclarableTemplateDecl *RT) {
+ return static_cast<const TemplateCommonAccess *>(RT)->Common != nullptr;
}
// True if CanonInjectedTST could still be cached later. Only valid to
// call once Common itself is confirmed to exist (see needToTrackCommonPtr).
static bool isTemplateCanonInjectedTSTValid(const ClassTemplateDecl *CTD) {
- return !canonInjectedTSTValid(*CTD);
+ auto *Ptr =
+ static_cast<const ClassTemplateCommonAccess *>(CTD)->getCommonPtr();
+ return !Ptr->CanonInjectedTST.isNull();
}
- // Write-once fields, fixed null prior state (Common/CanonInjectedTST
- // never existed before whichever PTU created them), so revert is a
+ // Write-once fields, fixed null prior state, so revert is a
// direct reset, not a snapshot restore.
static void resetTemplateCommonBase(RedeclarableTemplateDecl &RT) {
clearCommonPtr(RT);
@@ -349,8 +348,6 @@ class DeclStateReverter {
static void removeFromIdResolver(Sema &S, NamedDecl *D);
- // Walk D's redecl chain looking for the newest decl that predates this
- // PTU. Returns nullptr if the entire chain was created this PTU.
template <typename DeclT> DeclT *findSurvivor(DeclT *D, PTUID ID) const;
template <typename decl_type>
@@ -358,16 +355,12 @@ class DeclStateReverter {
setLatestRedecl<decl_type>(*D->getFirstDecl(), Survivor);
}
- /// Point the canonical decl's "most recent" link back at the newest
- /// redeclaration that predates this PTU.
void detachFromRedeclChain(const Decl *D, PTUID ID);
void repairLexicalChain(DeclContext &DC, PTUID ID);
private:
NamedDecl *tryDetachRedeclChain(Decl *D, PTUID ID);
-
- void removeFromLookupMap(NamedDecl *ND, DeclContext *DC, PTUID ID);
};
/// Maps addresses to the PTU that allocated them, by keeping the slab
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index fc9182f16aaa0..af88ca6725334 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -42,16 +42,6 @@ DeclStateReverter::createDefinitionDataFootprint(const CXXRecordDecl &RD) {
return FP;
}
-// bool DeclStateReverter::compareDefinitionDataFootprint(
-// const DefinitionDataFootprint &FP, const CXXRecordDecl &RD) {
-// const auto &Live = RD.data();
-// #define FIELD(Name, Width, Merge) \
-// if (FP.Name != Live.Name) \
-// return false;
-// #include "clang/AST/CXXRecordDeclDefinitionBits.def"
-// return true;
-// }
-
void DeclStateReverter::restoreDefinitionDataFootprint(
const DefinitionDataFootprint &FP, CXXRecordDecl &RD) {
auto &Live = RD.data();
@@ -156,9 +146,6 @@ void DeclStateReverter::detachCommonBase(const RedeclarableTemplateDecl *RT) {
clearCommonPtr(*R);
}
-void DeclStateReverter::removeFromLookupMap(NamedDecl *ND, DeclContext *DC,
- PTUID ID) {}
-
void DeclStateReverter::detachFromExternCLookup(Decl *D,
DeclContext *ExternCCtx,
PTUID ID) {
@@ -166,47 +153,46 @@ void DeclStateReverter::detachFromExternCLookup(Decl *D,
if (!ND)
return;
- bool IsExternC = false;
- if (const auto *FD = dyn_cast<FunctionDecl>(ND))
- IsExternC = FD->isExternC();
- else if (const auto *VD = dyn_cast<VarDecl>(ND))
- IsExternC = VD->isExternC();
+ // bool IsExternC = false;
+ // if (const auto *FD = dyn_cast<FunctionDecl>(ND))
+ // IsExternC = FD->isExternC();
+ // else if (const auto *VD = dyn_cast<VarDecl>(ND))
+ // IsExternC = VD->isExternC();
+
+ // if (IsExternC)
+ // removeFromLookupMap(ND, ExternCCtx, ID);
- if (IsExternC)
- removeFromLookupMap(ND, ExternCCtx, ID);
+ if (StoredDeclsMap *Map = ExternCCtx->getPrimaryContext()->getLookupPtr()) {
+ auto It = Map->find(ND->getDeclName());
+ if (It != Map->end())
+ It->second.remove(ND);
+ }
}
void DeclStateReverter::removeFromIdResolver(Sema &S, NamedDecl *D) {
+ if (D->getDeclName().isEmpty())
+ return;
if (D->getDeclName().getFETokenInfo())
S.IdResolver.RemoveDecl(D);
}
-/// Remove D from every lookup map it became visible in, reinstating the
-/// previous declaration where D had replaced one in-place (which is what
-/// StoredDeclsList::HandleRedeclaration does on a redeclaration --
-/// erasing the slot outright would lose the older decl entirely; that is
-/// the ReopenNs failure).
+/// Remove D from its DeclContext lookup map and restore the previous
+/// declaration if it was replaced.
void DeclStateReverter::detachFromDCLookup(Decl *D, PTUID ID) {
NamedDecl *ND = dyn_cast<NamedDecl>(D);
if (!ND)
- return; // nothing nameable -- never entered a lookup map
+ return;
- // Chain must still be intact to find the replacement -- that is why
- // this runs before detachFromRedeclChain.
+ // Find any declaration from the previous PTU in the redecl chain that may
+ // have been replaced by this declaration.
NamedDecl *Survivor = findSurvivor(ND, ID);
DeclarationName Name = ND->getDeclName();
- // A decl in a transparent context (an unnamed namespace, an unscoped
- // enum, a linkage-spec block) is visible in the enclosing context's map
- // as well, so every level it was inserted into needs the same repair --
- // not just its own primary context.
DeclContext *DC = ND->getDeclContext();
do {
DeclContext *Primary = DC->getPrimaryContext();
if (StoredDeclsMap *Map = Primary->getLookupPtr()) {
auto Pos = Map->find(Name);
- // Not an error: the map is built lazily, so a context that was
- // never looked up in has no entry for this name at all.
if (Pos != Map->end()) {
StoredDeclsList &List = Pos->second;
List.remove(ND);
@@ -219,7 +205,7 @@ void DeclStateReverter::detachFromDCLookup(Decl *D, PTUID ID) {
} while (DC->isTransparentContext() && (DC = DC->getParent()));
}
-// Walk D's redecl chain looking for the newest decl that predates this
+// Walk D's redecl chain looking for the newest decl that is from previous
// PTU. Returns nullptr if the entire chain was created this PTU.
template <typename DeclT>
DeclT *DeclStateReverter::findSurvivor(DeclT *D, PTUID ID) const {
@@ -421,15 +407,12 @@ uint32_t PTUMutationActions::DeclNeedingTracking(DeclShape S, const Decl *D) {
// already tracked
const RedeclarableTemplateDecl *RT =
cast<RedeclarableTemplateDecl>(D->getCanonicalDecl());
- if (!DeclStateReverter::isCommonPtrValid(*RT) &&
+ if (!DeclStateReverter::isCommonPtrValid(RT) &&
!HiddenMutationTracker.isTrackedFor(
RT, uint32_t(MutationType::TemplateCommon))) {
// No listener reports these Common changes, so use hidden tracking.
Kinds |= uint32_t(MutationType::TemplateCommon);
} else if (const auto *CTD = dyn_cast<ClassTemplateDecl>(RT)) {
- // Common exists, so calling getCommonPtr() here is safe (won't
- // allocate) -- only ClassTemplateDecl has CanonInjectedTST in its
- // own Common, not the Function/Var template siblings.
if (!DeclStateReverter::isTemplateCanonInjectedTSTValid(CTD))
Kinds |= uint32_t(MutationType::CanonInjectedTST);
}
@@ -567,7 +550,7 @@ uint32_t PTUMutationActions::confirmMutation(const Decl *D, DeclShape S,
Tracker.getHiddenMutationTracker().isTrackedFor(
RT, uint32_t(MutationType::TemplateCommon))) {
// No chain to compare against -- write-once.
- if (DeclStateReverter::isCommonPtrValid(*RT))
+ if (DeclStateReverter::isCommonPtrValid(RT))
Verified |= uint32_t(MutationType::TemplateCommon);
}
if (FlaggedKinds & uint32_t(MutationType::CanonInjectedTST) &&
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index 2c5a4d8489dc1..3cfabb5e5daa1 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -17,12 +17,8 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclContextInternals.h"
-<<<<<<< HEAD
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclVisitor.h"
-=======
-#include "clang/CodeGen/ModuleBuilder.h"
->>>>>>> 6d90b8675095 (Add Initial impl ErrorRecovery.h)
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/Parse/Parser.h"
>From 3162564c2861ef8759089a7c4baf239da999862c Mon Sep 17 00:00:00 2001
From: SahilPatidar <patidarsahil2001 at gmail.com>
Date: Thu, 1 Oct 2026 14:00:11 +0530
Subject: [PATCH 13/13] add missing header
---
.../include/clang/Interpreter/ErrorRecovery.h | 6 +--
clang/lib/Interpreter/ErrRecovery.cpp | 4 +-
llvm/include/llvm/Support/Allocator.h | 38 +++++++------------
3 files changed, 19 insertions(+), 29 deletions(-)
diff --git a/clang/include/clang/Interpreter/ErrorRecovery.h b/clang/include/clang/Interpreter/ErrorRecovery.h
index ee392b697af74..09edb0798b1ca 100644
--- a/clang/include/clang/Interpreter/ErrorRecovery.h
+++ b/clang/include/clang/Interpreter/ErrorRecovery.h
@@ -19,6 +19,8 @@
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
#include "llvm/ADT/BitVector.h"
#include <variant>
@@ -193,8 +195,6 @@ class DeclStateReverter {
using RedeclarableTemplateDecl::Common;
};
- // Common is `mutable`, so this is legal to call through a const
- // reference too -- no const_cast needed at any call site.
static void clearCommonPtr(const RedeclarableTemplateDecl &RT) {
static_cast<const TemplateCommonAccess &>(RT).Common = nullptr;
}
@@ -287,7 +287,7 @@ class DeclStateReverter {
}
// True if CanonInjectedTST could still be cached later. Only valid to
- // call once Common itself is confirmed to exist (see needToTrackCommonPtr).
+ // call once Common itself is confirmed to exist.
static bool isTemplateCanonInjectedTSTValid(const ClassTemplateDecl *CTD) {
auto *Ptr =
static_cast<const ClassTemplateCommonAccess *>(CTD)->getCommonPtr();
diff --git a/clang/lib/Interpreter/ErrRecovery.cpp b/clang/lib/Interpreter/ErrRecovery.cpp
index af88ca6725334..4f6b6e1798cab 100644
--- a/clang/lib/Interpreter/ErrRecovery.cpp
+++ b/clang/lib/Interpreter/ErrRecovery.cpp
@@ -12,11 +12,13 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Interpreter/ErrorRecovery.h"
+
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclContextInternals.h"
-#include "clang/Interpreter/ErrorRecovery.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/Sema/Sema.h"
namespace clang {
diff --git a/llvm/include/llvm/Support/Allocator.h b/llvm/include/llvm/Support/Allocator.h
index 56bce4d31c8a7..832e6b1d16c30 100644
--- a/llvm/include/llvm/Support/Allocator.h
+++ b/llvm/include/llvm/Support/Allocator.h
@@ -250,39 +250,27 @@ class BumpPtrAllocatorImpl
size_t GetNumSlabs() const { return Slabs.size() + CustomSizedSlabs.size(); }
SlabCheckPoint CheckPoint() const {
- return {static_cast<unsigned int>(Slabs.empty() ? 0u : (Slabs.size() - 1)),
+ return {Slabs.empty() ? 0u : static_cast<unsigned int>(Slabs.size() - 1),
CurPtr, static_cast<unsigned int>(CustomSizedSlabs.size())};
}
bool isAfterCheckpoint(const void *Ptr, const SlabCheckPoint &CP) const {
const char *P = static_cast<const char *>(Ptr);
- // Regular (shared, bump-allocated) slabs.
- if (!CP.CurPtr) {
- // Checkpoint predates this allocator's first regular slab -- every
- // regular slab that exists now was allocated after it.
- for (unsigned I = 0; I < Slabs.size(); ++I) {
- const char *Start = static_cast<const char *>(Slabs[I]);
- if (P >= Start && P < Start + computeSlabSize(I))
- return true;
- }
- } else {
- if (CP.SlabIdx < Slabs.size()) {
- const char *Start = static_cast<const char *>(Slabs[CP.SlabIdx]);
- if (P >= CP.CurPtr && P < Start + computeSlabSize(CP.SlabIdx))
- return true;
- }
- for (unsigned I = CP.SlabIdx + 1; I < Slabs.size(); ++I) {
- const char *Start = static_cast<const char *>(Slabs[I]);
- if (P >= Start && P < (Start + computeSlabSize(I)))
- return true;
- }
+ // From the checkpoint's slab onward, memory after CurPtr is new.
+ // All later slabs are new. If CurPtr is null, slab 0 is all new.
+ for (unsigned I = CP.SlabIdx, E = Slabs.size(); I < E; ++I) {
+ const char *S = static_cast<const char *>(Slabs[I]);
+ const char *Lo = (I == CP.SlabIdx && CP.CurPtr) ? CP.CurPtr : S;
+ if (P >= Lo && P < S + computeSlabSize(I))
+ return true;
}
- for (unsigned I = CP.CustomSlabCount; I < CustomSizedSlabs.size(); ++I) {
- const char *Start = static_cast<const char *>(CustomSizedSlabs[I].first);
- size_t Size = CustomSizedSlabs[I].second;
- if (P >= Start && P < Start + Size)
+ // Custom-sized slabs are allocated whole, so the index alone decides.
+ for (unsigned I = CP.CustomSlabCount, E = CustomSizedSlabs.size(); I < E;
+ ++I) {
+ const char *S = static_cast<const char *>(CustomSizedSlabs[I].first);
+ if (P >= S && P < S + CustomSizedSlabs[I].second)
return true;
}
More information about the llvm-commits
mailing list