================
@@ -191,30 +195,150 @@ void IncrementalParser::withdrawMostRecentTU(
C.TUDecl = Prev;
}
+/// Removes decls introduced in the discarding PTU and restores the
+/// redeclaration chain to previous state.
+class ASTDeclUnmerger : public DeclVisitor<ASTDeclUnmerger> {
+ ASTContext &Ctx;
+ TranslationUnitDecl *DiscardedTU;
+
+public:
+ template <typename DeclT> void withdraw(Redeclarable<DeclT> *DBase) {
+ if (NamedDecl *Prev = findSurvivor(static_cast<DeclT *>(DBase)))
+ unlinkRedeclChain(Ctx, DBase, Prev);
+ }
+
+ /// The newest declaration of whatever D redeclares that still lives outside
+ /// the DiscardedTU, or null if DiscardedTU introduced the name.
+ NamedDecl *findSurvivor(NamedDecl *D) const {
+ for (Decl *Prev = D->getPreviousDecl(); Prev;
+ Prev = Prev->getPreviousDecl())
+ if (Prev->getTranslationUnitDecl() != DiscardedTU)
+ return dyn_cast<NamedDecl>(Prev);
+ return nullptr;
+ }
+
+ template <typename DeclT>
+ void unlinkRedeclChain(ASTContext &C, Redeclarable<DeclT> *DBase,
+ NamedDecl *PrevND) {
+ auto *Latest = static_cast<DeclT *>(DBase);
+ auto *Survivor = cast<DeclT>(PrevND);
+
+ // Rebuild First -> ... -> Survivor -> ... -> Latest as
+ // First -> ... -> Survivor.
+ Latest->getFirstDecl()->RedeclLink.setLatest(Survivor);
+
+ // The chain is circular: a withdrawn declaration still linked into it can
+ // never walk back around to itself, so redecls() on one would not
+ // terminate. Give each withdrawn declaration a chain of its own.
+ for (DeclT *Dead = Latest; Dead != Survivor;) {
+ DeclT *Next = Dead->getPreviousDecl();
+ Dead->First = Dead;
+ Dead->RedeclLink = Redeclarable<DeclT>::LatestDeclLink(C);
+ Dead = Next;
+ }
+ }
+
+ ASTDeclUnmerger(ASTContext &Ctx, TranslationUnitDecl *DiscardedTU)
+ : Ctx(Ctx), DiscardedTU(DiscardedTU) {}
+
+ // Kinds that are not redeclarable have no chain to repair.
+ void VisitDecl(Decl *) {}
+
+ void VisitFunctionDecl(FunctionDecl *D) { withdraw(D); }
+ void VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { withdraw(D); }
+ void VisitTypedefNameDecl(TypedefNameDecl *D) { withdraw(D); }
+ void VisitUsingShadowDecl(UsingShadowDecl *D) { withdraw(D); }
+ void VisitVarDecl(VarDecl *D) { withdraw(D); }
+
+ void VisitTagDecl(TagDecl *D) {
+ NamedDecl *Prev = findSurvivor(D);
+ if (!Prev)
+ return;
+ unlinkRedeclChain(Ctx, D, Prev);
+
+ // A class definition kept in DefinitionData outside the redeclaration
chain
+ auto *RD = dyn_cast<CXXRecordDecl>(Prev);
+ if (!RD)
+ return;
+ if (CXXRecordDecl *Def = RD->getDefinition();
+ Def && Def->getTranslationUnitDecl() == DiscardedTU)
+ for (auto *R : RD->redecls())
+ cast<CXXRecordDecl>(R)->DefinitionData = nullptr;
+ }
+
+ void VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
+ withdraw(D);
+ Visit(D->getTemplatedDecl());
+ }
+
+ void VisitNamespaceDecl(NamespaceDecl *D) {
+ // Handle cases of nested redeclarations like:
+ // PTU1: namespace outer { namespace ns { class Foo; } }
+ // PTU2: namespace outer { namespace ns { class Foo { ... }; error; } }
+ // Foo's redeclaration needs to be restored
+ llvm::SmallVector<NamedDecl *, 8> Survivors;
+ for (Decl *M : D->decls()) {
+ if (auto *ND = dyn_cast<NamedDecl>(M))
+ if (NamedDecl *Prev = findSurvivor(ND))
+ Survivors.push_back(Prev);
+ Visit(M);
+ D->removeDecl(M);
+ }
+
+ // A re-opened namespace makes its members visible in the namespace's
+ // primary context, which outlives the discarded unit
+ DeclContext *Primary = D->getPrimaryContext();
+ for (NamedDecl *Prev : Survivors)
+ Primary->makeDeclVisibleInContext(Prev);
+
+ withdraw(D);
+ }
+};
+
void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
+ ASTDeclUnmerger Unmerger(S.getASTContext(), MostRecentTU);
+
if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr())
{
----------------
vgvassilev wrote:
All this logic here needs to move to in the visitor where we iterate over the
content of `MostRecentTU` and decide how to handle each declaration depending
on its kind. That's how cling currently does it.
https://github.com/llvm/llvm-project/pull/218149
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits