https://github.com/Vipul-Cariappa updated 
https://github.com/llvm/llvm-project/pull/218149

>From 6c457a43df5d235a0271f17af4c87423d8520a09 Mon Sep 17 00:00:00 2001
From: Vipul Cariappa <[email protected]>
Date: Wed, 2 Sep 2026 11:08:40 +0530
Subject: [PATCH 1/2] [clang-repl] Keep earlier declarations alive when an
 input fails

Fixes llvm#201844.
---
 clang/include/clang/AST/DeclCXX.h             |   1 +
 clang/lib/Interpreter/IncrementalParser.cpp   | 131 +++++++++++++++++-
 clang/lib/Interpreter/IncrementalParser.h     |  21 +++
 .../failed-input-keeps-redecls.cpp            |  70 ++++++++++
 4 files changed, 220 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/Interpreter/failed-input-keeps-redecls.cpp

diff --git a/clang/include/clang/AST/DeclCXX.h 
b/clang/include/clang/AST/DeclCXX.h
index ff2223070dc15..3673738408e55 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -264,6 +264,7 @@ class CXXRecordDecl : public RecordDecl {
   friend class ASTRecordWriter;
   friend class ASTWriter;
   friend class DeclContext;
+  friend class IncrementalParser;
   friend class LambdaExpr;
   friend class ODRDiagsEmitter;
 
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp 
b/clang/lib/Interpreter/IncrementalParser.cpp
index 12beb542572d7..e2cdeebbe8de7 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -15,7 +15,13 @@
 
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclContextInternals.h"
+#include "clang/AST/DeclFriend.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclOpenACC.h"
+#include "clang/AST/DeclOpenMP.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"
 #include "clang/Parse/Parser.h"
@@ -191,23 +197,140 @@ void IncrementalParser::withdrawMostRecentTU(
   C.TUDecl = Prev;
 }
 
+/// Returns 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.
+static void dropContainingMembers(NamespaceDecl *ND) {
+  llvm::SmallVector<Decl *, 8> Members(ND->decls());
+  for (Decl *M : Members)
+    ND->removeDecl(M);
+}
+
+template <typename DeclT>
+void IncrementalParser::unlinkRedeclChain(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.
+  ASTContext &C = S.getASTContext();
+  for (DeclT *Dead = Latest; Dead != Survivor;) {
+    DeclT *Next = Dead->getPreviousDecl();
+    Dead->First = Dead;
+    Dead->RedeclLink = Redeclarable<DeclT>::LatestDeclLink(C);
+    Dead = Next;
+  }
+}
+
+template <typename DeclT>
+void IncrementalParser::withdrawRedeclImpl(Redeclarable<DeclT> *D,
+                                           NamedDecl *Prev,
+                                           TranslationUnitDecl *) {
+  unlinkRedeclChain(D, Prev);
+}
+
+template <>
+void IncrementalParser::withdrawRedeclImpl(Redeclarable<TagDecl> *D,
+                                           NamedDecl *Prev,
+                                           TranslationUnitDecl *DiscardedTU) {
+  unlinkRedeclChain(D, Prev);
+
+  // A class keeps its definition outside the redeclaration chain.
+  // If the definition was provided in the DiscardedTU, drop it.
+  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;
+}
+
+template <>
+void IncrementalParser::withdrawRedeclImpl(Redeclarable<NamespaceDecl> *D,
+                                           NamedDecl *Prev,
+                                           TranslationUnitDecl *) {
+  dropContainingMembers(static_cast<NamespaceDecl *>(D));
+  unlinkRedeclChain(D, Prev);
+}
+
+template <>
+void IncrementalParser::withdrawRedeclImpl(
+    Redeclarable<RedeclarableTemplateDecl> *D, NamedDecl *Prev,
+    TranslationUnitDecl *DiscardedTU) {
+  unlinkRedeclChain(D, Prev);
+
+  // The pattern a template declares keeps a redeclaration chain of its own,
+  // running alongside the template's.
+  auto *RTD = static_cast<RedeclarableTemplateDecl *>(D);
+  auto *PrevRTD = cast<RedeclarableTemplateDecl>(Prev);
+  withdrawRedecl(RTD->getTemplatedDecl(), PrevRTD->getTemplatedDecl(),
+                 DiscardedTU);
+}
+
+void IncrementalParser::withdrawRedeclImpl(...) {
+  llvm_unreachable("withdrawRedecl on a non-redeclarable declaration");
+}
+
+void IncrementalParser::withdrawRedecl(NamedDecl *D, NamedDecl *Prev,
+                                       TranslationUnitDecl *DiscardedTU) {
+  switch (D->getKind()) {
+#define ABSTRACT_DECL(TYPE)
+#define DECL(TYPE, BASE)                                                       
\
+  case Decl::TYPE:                                                             
\
+    withdrawRedeclImpl(cast<TYPE##Decl>(D), Prev, DiscardedTU);                
\
+    break;
+#include "clang/AST/DeclNodes.inc"
+  }
+}
+
 void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
   if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) 
{
     // Collect the keys to erase: erasing during iteration invalidates the map
     // iterator under backward-shift deletion.
     llvm::SmallVector<DeclarationName, 16> KeysToErase;
+    // Declarations an earlier input made and this one only redeclared
+    llvm::SmallVector<std::pair<DeclarationName, NamedDecl *>, 4>
+        DeclsToRestore;
     for (auto &&[Key, List] : *Map) {
       DeclContextLookupResult R = List.getLookupResult();
       std::vector<NamedDecl *> NamedDeclsToRemove;
       bool RemoveAll = true;
       for (NamedDecl *D : R) {
-        if (D->getTranslationUnitDecl() == MostRecentTU)
-          NamedDeclsToRemove.push_back(D);
-        else
+        if (D->getTranslationUnitDecl() != MostRecentTU) {
           RemoveAll = false;
+          continue;
+        }
+        NamedDeclsToRemove.push_back(D);
       }
+      // Dropping the lookup entries is not enough, also remove them from the
+      // redeclare chain.
+      llvm::SmallVector<NamedDecl *, 4> Survivors;
+      for (NamedDecl *D : NamedDeclsToRemove) {
+        if (NamedDecl *Prev = findSurvivingPrevDecl(D, MostRecentTU)) {
+          withdrawRedecl(D, Prev, MostRecentTU);
+          Survivors.push_back(Prev);
+        }
+      }
+
       if (LLVM_LIKELY(RemoveAll)) {
         KeysToErase.push_back(Key);
+        for (NamedDecl *Prev : Survivors)
+          DeclsToRestore.emplace_back(Key, Prev);
       } else {
         for (NamedDecl *D : NamedDeclsToRemove)
           List.remove(D);
@@ -215,6 +338,8 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl 
*MostRecentTU) {
     }
     for (DeclarationName Key : KeysToErase)
       Map->erase(Key);
+    for (auto &[Key, Prev] : DeclsToRestore)
+      (*Map)[Key].addOrReplaceDecl(Prev);
   }
 
   // Check if we need to clean up the IdResolver chain.
diff --git a/clang/lib/Interpreter/IncrementalParser.h 
b/clang/lib/Interpreter/IncrementalParser.h
index b626cebaafcd7..0f6ce4587219e 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -25,7 +25,9 @@ class ASTConsumer;
 class CompilerInstance;
 class Parser;
 class Sema;
+class NamedDecl;
 class TranslationUnitDecl;
+template <typename decl_type> class Redeclarable;
 class IncrementalAction;
 struct PartialTranslationUnit;
 
@@ -73,6 +75,25 @@ class IncrementalParser {
   /// Rebuild the translation unit redeclaration chain without \p MostRecentTU,
   /// making its predecessor the current unit again.
   void withdrawMostRecentTU(TranslationUnitDecl *MostRecentTU);
+
+  /// Rebuild D's redeclaration chain without the declarations
+  /// DiscardedTU contributed, making Prev current again.
+  void withdrawRedecl(NamedDecl *D, NamedDecl *Prev,
+                      TranslationUnitDecl *DiscardedTU);
+
+  /// Unlink D from the chain it shares with Prev.
+  template <typename DeclT>
+  void unlinkRedeclChain(Redeclarable<DeclT> *D, NamedDecl *Prev);
+
+  /// Withdraw one redeclarable declaration. Kinds that keep state outside the
+  /// redeclaration chain specialize this.
+  template <typename DeclT>
+  void withdrawRedeclImpl(Redeclarable<DeclT> *D, NamedDecl *Prev,
+                          TranslationUnitDecl *DiscardedTU);
+
+  /// Fallback for the non-redeclarable kinds the DeclNodes.inc switch also
+  /// enumerates.
+  void withdrawRedeclImpl(...);
 };
 } // end namespace clang
 
diff --git a/clang/test/Interpreter/failed-input-keeps-redecls.cpp 
b/clang/test/Interpreter/failed-input-keeps-redecls.cpp
new file mode 100644
index 0000000000000..ff609f3255b03
--- /dev/null
+++ b/clang/test/Interpreter/failed-input-keeps-redecls.cpp
@@ -0,0 +1,70 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl 2>&1 | FileCheck %s
+// RUN: cat %s | clang-repl 2>&1 | FileCheck %s --check-prefix=NEG
+
+// A failed input must not take earlier declarations down with it, and must not
+// leave anything of its own behind for a later input to trip over.
+
+extern "C" int printf(const char *, ...);
+
+namespace N { struct S { int v; }; void foo() { printf("foo\n"); } }
+
+namespace N { void bar() { printf("bar\n" } }
+// CHECK-DAG: error: expected ')'
+
+// Everything N held before the failed input is still reachable.
+N::foo();
+// CHECK-DAG: foo
+N::S s; s.v = 7; printf("s.v = %d\n", s.v);
+// CHECK-DAG: s.v = 7
+
+// N is still open for business, and bar is free to be defined properly.
+namespace N { void bar() { printf("bar\n"); } }
+N::bar();
+// CHECK-DAG: bar
+// NEG-NOT: error: call to 'bar' is ambiguous
+
+namespace N { void baz() { printf("baz\n"); } }
+N::baz();
+// CHECK-DAG: baz
+
+// A name that only ever existed in a failed input stays gone.
+namespace M { int m = undeclared_thing; }
+// CHECK-DAG: error: use of undeclared identifier 'undeclared_thing'
+int probe = M::m;
+// CHECK-DAG: error: use of undeclared identifier 'M'
+
+// A class survives a failed redefinition, and the failed definition does not
+// become the one everybody sees.
+struct T;
+struct T { int a; }; int e1 = undeclared_thing;
+// CHECK-DAG: error: use of undeclared identifier 'undeclared_thing'
+T *tp = nullptr; printf("T reachable %d\n", tp == nullptr);
+// CHECK-DAG: T reachable 1
+struct T { int a; int b; };
+printf("sizeof(T) = %d\n", (int)sizeof(T));
+// CHECK-DAG: sizeof(T) = 
+
+enum E : int;
+enum E : int { A = 1 }; int e2 = undeclared_thing;
+// CHECK-DAG: error: use of undeclared identifier 'undeclared_thing'
+enum E : int { A = 1, B = 2 };
+printf("B = %d\n", (int)B);
+// CHECK-DAG: B = 2
+
+// Kinds reached only through the generated switch, not by any hand-written
+// list: a namespace alias and a using declaration.
+namespace Deep { int v = 11; void g() { printf("Deep::g\n"); } }
+namespace Al = Deep;
+namespace Al = Deep; int e8 = undeclared_thing;
+// CHECK-DAG: error: use of undeclared identifier 'undeclared_thing'
+printf("Al::v = %d\n", Al::v);
+// CHECK-DAG: Al::v = 11
+
+using Deep::g;
+using Deep::g; int e9 = undeclared_thing;
+// CHECK-DAG: error: use of undeclared identifier 'undeclared_thing'
+g();
+// CHECK-DAG: Deep::g
+
+%quit

>From 875a66056cf451ef46668471ecadcfc15d015ffa Mon Sep 17 00:00:00 2001
From: Vipul Cariappa <[email protected]>
Date: Tue, 22 Sep 2026 11:24:54 +0530
Subject: [PATCH 2/2] Implement restoration of redeclartion chain as ASTVisitor

Also fix the case of nested redeclarations.
---
 clang/include/clang/AST/DeclCXX.h             |   2 +-
 clang/include/clang/AST/Redeclarable.h        |   1 +
 clang/lib/Interpreter/IncrementalParser.cpp   | 191 +++++++++---------
 clang/lib/Interpreter/IncrementalParser.h     |  21 --
 .../failed-input-keeps-redecls.cpp            |  28 +++
 5 files changed, 125 insertions(+), 118 deletions(-)

diff --git a/clang/include/clang/AST/DeclCXX.h 
b/clang/include/clang/AST/DeclCXX.h
index 3673738408e55..6cb9984f629ac 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -257,6 +257,7 @@ class CXXBaseSpecifier {
 /// Represents a C++ struct/union/class.
 class CXXRecordDecl : public RecordDecl {
   friend class ASTDeclMerger;
+  friend class ASTDeclUnmerger;
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
   friend class ASTNodeImporter;
@@ -264,7 +265,6 @@ class CXXRecordDecl : public RecordDecl {
   friend class ASTRecordWriter;
   friend class ASTWriter;
   friend class DeclContext;
-  friend class IncrementalParser;
   friend class LambdaExpr;
   friend class ODRDiagsEmitter;
 
diff --git a/clang/include/clang/AST/Redeclarable.h 
b/clang/include/clang/AST/Redeclarable.h
index 35911ee2f7d16..fe2648aa58301 100644
--- a/clang/include/clang/AST/Redeclarable.h
+++ b/clang/include/clang/AST/Redeclarable.h
@@ -186,6 +186,7 @@ class Redeclarable {
 
 public:
   friend class ASTDeclMerger;
+  friend class ASTDeclUnmerger;
   friend class ASTDeclReader;
   friend class ASTDeclWriter;
   friend class IncrementalParser;
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp 
b/clang/lib/Interpreter/IncrementalParser.cpp
index e2cdeebbe8de7..694ce2e81c91e 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -18,10 +18,8 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclContextInternals.h"
 #include "clang/AST/DeclFriend.h"
-#include "clang/AST/DeclObjC.h"
-#include "clang/AST/DeclOpenACC.h"
-#include "clang/AST/DeclOpenMP.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DeclVisitor.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Interpreter/PartialTranslationUnit.h"
 #include "clang/Parse/Parser.h"
@@ -197,108 +195,109 @@ void IncrementalParser::withdrawMostRecentTU(
   C.TUDecl = Prev;
 }
 
-/// Returns 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.
-static void dropContainingMembers(NamespaceDecl *ND) {
-  llvm::SmallVector<Decl *, 8> Members(ND->decls());
-  for (Decl *M : Members)
-    ND->removeDecl(M);
-}
+/// Removes decls introduced in the discarding PTU and restores the
+/// redeclaration chain to previous state.
+class ASTDeclUnmerger : public DeclVisitor<ASTDeclUnmerger> {
+  ASTContext &Ctx;
+  TranslationUnitDecl *DiscardedTU;
 
-template <typename DeclT>
-void IncrementalParser::unlinkRedeclChain(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.
-  ASTContext &C = S.getASTContext();
-  for (DeclT *Dead = Latest; Dead != Survivor;) {
-    DeclT *Next = Dead->getPreviousDecl();
-    Dead->First = Dead;
-    Dead->RedeclLink = Redeclarable<DeclT>::LatestDeclLink(C);
-    Dead = Next;
+public:
+  template <typename DeclT> void withdraw(Redeclarable<DeclT> *DBase) {
+    if (NamedDecl *Prev = findSurvivor(static_cast<DeclT *>(DBase)))
+      unlinkRedeclChain(Ctx, DBase, Prev);
   }
-}
 
-template <typename DeclT>
-void IncrementalParser::withdrawRedeclImpl(Redeclarable<DeclT> *D,
-                                           NamedDecl *Prev,
-                                           TranslationUnitDecl *) {
-  unlinkRedeclChain(D, 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 <>
-void IncrementalParser::withdrawRedeclImpl(Redeclarable<TagDecl> *D,
-                                           NamedDecl *Prev,
-                                           TranslationUnitDecl *DiscardedTU) {
-  unlinkRedeclChain(D, Prev);
+  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;
+    }
+  }
 
-  // A class keeps its definition outside the redeclaration chain.
-  // If the definition was provided in the DiscardedTU, drop it.
-  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;
-}
+  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;
+  }
 
-template <>
-void IncrementalParser::withdrawRedeclImpl(Redeclarable<NamespaceDecl> *D,
-                                           NamedDecl *Prev,
-                                           TranslationUnitDecl *) {
-  dropContainingMembers(static_cast<NamespaceDecl *>(D));
-  unlinkRedeclChain(D, Prev);
-}
+  void VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
+    withdraw(D);
+    Visit(D->getTemplatedDecl());
+  }
 
-template <>
-void IncrementalParser::withdrawRedeclImpl(
-    Redeclarable<RedeclarableTemplateDecl> *D, NamedDecl *Prev,
-    TranslationUnitDecl *DiscardedTU) {
-  unlinkRedeclChain(D, Prev);
-
-  // The pattern a template declares keeps a redeclaration chain of its own,
-  // running alongside the template's.
-  auto *RTD = static_cast<RedeclarableTemplateDecl *>(D);
-  auto *PrevRTD = cast<RedeclarableTemplateDecl>(Prev);
-  withdrawRedecl(RTD->getTemplatedDecl(), PrevRTD->getTemplatedDecl(),
-                 DiscardedTU);
-}
+  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);
+    }
 
-void IncrementalParser::withdrawRedeclImpl(...) {
-  llvm_unreachable("withdrawRedecl on a non-redeclarable declaration");
-}
+    // 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);
 
-void IncrementalParser::withdrawRedecl(NamedDecl *D, NamedDecl *Prev,
-                                       TranslationUnitDecl *DiscardedTU) {
-  switch (D->getKind()) {
-#define ABSTRACT_DECL(TYPE)
-#define DECL(TYPE, BASE)                                                       
\
-  case Decl::TYPE:                                                             
\
-    withdrawRedeclImpl(cast<TYPE##Decl>(D), Prev, DiscardedTU);                
\
-    break;
-#include "clang/AST/DeclNodes.inc"
+    withdraw(D);
   }
-}
+};
 
 void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
+  ASTDeclUnmerger Unmerger(S.getASTContext(), MostRecentTU);
+
   if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) 
{
     // Collect the keys to erase: erasing during iteration invalidates the map
     // iterator under backward-shift deletion.
@@ -321,10 +320,10 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl 
*MostRecentTU) {
       // redeclare chain.
       llvm::SmallVector<NamedDecl *, 4> Survivors;
       for (NamedDecl *D : NamedDeclsToRemove) {
-        if (NamedDecl *Prev = findSurvivingPrevDecl(D, MostRecentTU)) {
-          withdrawRedecl(D, Prev, MostRecentTU);
+        NamedDecl *Prev = Unmerger.findSurvivor(D);
+        Unmerger.Visit(D);
+        if (Prev)
           Survivors.push_back(Prev);
-        }
       }
 
       if (LLVM_LIKELY(RemoveAll)) {
diff --git a/clang/lib/Interpreter/IncrementalParser.h 
b/clang/lib/Interpreter/IncrementalParser.h
index 0f6ce4587219e..b626cebaafcd7 100644
--- a/clang/lib/Interpreter/IncrementalParser.h
+++ b/clang/lib/Interpreter/IncrementalParser.h
@@ -25,9 +25,7 @@ class ASTConsumer;
 class CompilerInstance;
 class Parser;
 class Sema;
-class NamedDecl;
 class TranslationUnitDecl;
-template <typename decl_type> class Redeclarable;
 class IncrementalAction;
 struct PartialTranslationUnit;
 
@@ -75,25 +73,6 @@ class IncrementalParser {
   /// Rebuild the translation unit redeclaration chain without \p MostRecentTU,
   /// making its predecessor the current unit again.
   void withdrawMostRecentTU(TranslationUnitDecl *MostRecentTU);
-
-  /// Rebuild D's redeclaration chain without the declarations
-  /// DiscardedTU contributed, making Prev current again.
-  void withdrawRedecl(NamedDecl *D, NamedDecl *Prev,
-                      TranslationUnitDecl *DiscardedTU);
-
-  /// Unlink D from the chain it shares with Prev.
-  template <typename DeclT>
-  void unlinkRedeclChain(Redeclarable<DeclT> *D, NamedDecl *Prev);
-
-  /// Withdraw one redeclarable declaration. Kinds that keep state outside the
-  /// redeclaration chain specialize this.
-  template <typename DeclT>
-  void withdrawRedeclImpl(Redeclarable<DeclT> *D, NamedDecl *Prev,
-                          TranslationUnitDecl *DiscardedTU);
-
-  /// Fallback for the non-redeclarable kinds the DeclNodes.inc switch also
-  /// enumerates.
-  void withdrawRedeclImpl(...);
 };
 } // end namespace clang
 
diff --git a/clang/test/Interpreter/failed-input-keeps-redecls.cpp 
b/clang/test/Interpreter/failed-input-keeps-redecls.cpp
index ff609f3255b03..d7fd37fa7c2f6 100644
--- a/clang/test/Interpreter/failed-input-keeps-redecls.cpp
+++ b/clang/test/Interpreter/failed-input-keeps-redecls.cpp
@@ -67,4 +67,32 @@ using Deep::g; int e9 = undeclared_thing;
 g();
 // CHECK-DAG: Deep::g
 
+// A member of a re-opened namespace is a redeclaration in its own right, and
+// needs the same treatment as one at the top level: dropping it from the
+// namespace must put back what the name meant before.
+namespace ns { class Foo; }
+namespace ns { class Foo { public: int v; }; int e10 = undeclared_thing; }
+// CHECK-DAG: error: use of undeclared identifier 'undeclared_thing'
+ns::Foo *fp = nullptr; printf("ns::Foo reachable %d\n", fp == nullptr);
+// CHECK-DAG: ns::Foo reachable 1
+namespace ns { class Foo { public: int v; int w; }; }
+ns::Foo foo; foo.v = 1; foo.w = 2; printf("foo = %d %d\n", foo.v, foo.w);
+// CHECK-DAG: foo = 1 2
+
+namespace ns { void h(); }
+namespace ns { void h() { printf("h discarded\n"); } int e11 = 
undeclared_thing; }
+// CHECK-DAG: error: use of undeclared identifier 'undeclared_thing'
+namespace ns { void h() { printf("h kept\n"); } }
+ns::h();
+// CHECK-DAG: h kept
+// NEG-NOT: {{^}}h discarded
+
+// The same, one namespace deeper: the inner namespace is itself a member of
+// the outer one.
+namespace outer { namespace inner { class Bar; } }
+namespace outer { namespace inner { class Bar { public: int v; }; } int e12 = 
undeclared_thing; }
+// CHECK-DAG: error: use of undeclared identifier 'undeclared_thing'
+outer::inner::Bar *bp = nullptr; printf("outer::inner::Bar reachable %d\n", bp 
== nullptr);
+// CHECK-DAG: outer::inner::Bar reachable 1
+
 %quit

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to