================
@@ -191,39 +199,136 @@ void IncrementalParser::withdrawMostRecentTU(
   C.TUDecl = Prev;
 }
 
+/// The newest declaration of whatever D redeclares that still lives outside
+/// DiscardedTU
+static NamedDecl *findSurvivingPrevDecl(NamedDecl *D,
+                                        TranslationUnitDecl *DiscardedTU) {
+  for (Decl *Prev = D->getPreviousDecl(); Prev; Prev = Prev->getPreviousDecl())
+    if (Prev->getTranslationUnitDecl() != DiscardedTU)
+      return dyn_cast<NamedDecl>(Prev);
+  return nullptr;
+}
+
+/// Unlink everything a discarded re-opening of a namespace put into it. 
Members
+/// are made visible in the namespace's primary context, which outlives the
+/// discarded PTU, so leaving them behind would keep half-parsed declarations
+/// reachable
+static void dropContainingMembers(NamespaceDecl *ND) {
+  llvm::SmallVector<Decl *, 8> Members(ND->decls());
+  for (Decl *M : Members)
+    ND->removeDecl(M);
+}
+
+bool IncrementalParser::withdrawRedecl(NamedDecl *D, NamedDecl *Prev,
+                                       TranslationUnitDecl *DiscardedTU) {
+  ASTContext &C = S.getASTContext();
+
+  auto Unlink = [&C](auto *Latest, NamedDecl *SurvivorND) {
+    using T = std::remove_pointer_t<decltype(Latest)>;
+    auto *Survivor = cast<T>(SurvivorND);
+
+    // 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 a chain of its own.
+    for (T *Dead = Latest; Dead != Survivor;) {
+      T *Next = Dead->getPreviousDecl();
+      Dead->First = Dead;
+      Dead->RedeclLink = Redeclarable<T>::LatestDeclLink(C);
+      Dead = Next;
+    }
+  };
+
+  if (auto *TD = dyn_cast<TagDecl>(D)) {
+    Unlink(TD, Prev);
+    // A class keeps its definition outside the redeclaration chain, in the
+    // DefinitionData that startDefinition() hands to every redeclaration.
+    // Unlinking leaves the survivors pointing at the discarded definition, so
+    // drop it; a later definition allocates a fresh one for the whole chain.
+    if (auto *RD = dyn_cast<CXXRecordDecl>(Prev))
+      if (CXXRecordDecl *Def = RD->getDefinition();
+          Def && Def->getTranslationUnitDecl() == DiscardedTU)
+        for (auto *R : RD->redecls())
+          cast<CXXRecordDecl>(R)->DefinitionData = nullptr;
+    return true;
+  }
+  if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+    Unlink(FD, Prev);
+    return true;
+  }
+  if (auto *VD = dyn_cast<VarDecl>(D)) {
+    Unlink(VD, Prev);
+    return true;
+  }
+  if (auto *ND = dyn_cast<NamespaceDecl>(D)) {
+    dropContainingMembers(ND);
+    Unlink(ND, Prev);
+    return true;
+  }
+  if (auto *TND = dyn_cast<TypedefNameDecl>(D)) {
+    Unlink(TND, Prev);
+    return true;
----------------
vgvassilev wrote:

I still think this should become a tiny visitor in this file modelling how 
things in the ASTReader are done.

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