================
@@ -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

Reply via email to