llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Vipul Cariappa (Vipul-Cariappa)

<details>
<summary>Changes</summary>

Fixes #<!-- -->201844

@<!-- -->vgvassilev, this is required for the new overload resolution. We 
should try to land this in the LLVM 23 release.

---
Full diff: https://github.com/llvm/llvm-project/pull/218149.diff


2 Files Affected:

- (modified) clang/lib/Interpreter/IncrementalParser.cpp (+49-4) 
- (added) clang/test/Interpreter/failed-input-keeps-redecls.cpp (+41) 


``````````diff
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp 
b/clang/lib/Interpreter/IncrementalParser.cpp
index 59018907056b8..6ad23ad89066e 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -191,23 +191,62 @@ 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);
+}
+
 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;
+    // Decls with a previous declaration that needs to be restored, instead of
+    // discarding.
+    llvm::SmallVector<std::pair<DeclarationName, NamedDecl *>, 4>
+        DeclsToRestore;
+    // List of namespaces to whose members needs to be dropped,
+    // but not the namespace itself, as the this is a redeclaration of it.
+    llvm::SmallVector<NamespaceDecl *, 4> NamespacesToDrain;
     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);
+        if (auto *NS = dyn_cast<NamespaceDecl>(D);
+            NS && NS->getCanonicalDecl()->getTranslationUnitDecl() !=
+                      MostRecentTU)
+          NamespacesToDrain.push_back(NS);
       }
       if (LLVM_LIKELY(RemoveAll)) {
-        KeysToErase.push_back(Key);
+        bool Restored = false;
+        for (NamedDecl *D : NamedDeclsToRemove)
+          if (NamedDecl *Prev = findSurvivingPrevDecl(D, MostRecentTU)) {
+            DeclsToRestore.emplace_back(Key, Prev);
+            Restored = true;
+          }
+        if (!Restored)
+          KeysToErase.push_back(Key);
       } else {
         for (NamedDecl *D : NamedDeclsToRemove)
           List.remove(D);
@@ -215,6 +254,12 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl 
*MostRecentTU) {
     }
     for (DeclarationName Key : KeysToErase)
       Map->erase(Key);
+    for (auto &[Key, Prev] : DeclsToRestore) {
+      Map->erase(Key);
+      (*Map)[Key].addOrReplaceDecl(Prev);
+    }
+    for (NamespaceDecl *ND : NamespacesToDrain)
+      dropContainingMembers(ND);
   }
 
   ExternCContextDecl *ECCD = S.getASTContext().getExternCContextDecl();
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..97ebd151700b3
--- /dev/null
+++ b/clang/test/Interpreter/failed-input-keeps-redecls.cpp
@@ -0,0 +1,41 @@
+// REQUIRES: host-supports-jit
+// RUN: cat %s | clang-repl 2>&1 | FileCheck %s
+
+// A failed input must not take earlier declarations down with it.
+
+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
+
+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, likewise, survives a failed redefinition.
+struct T;
+struct T { int v = 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
+
+%quit

``````````

</details>


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