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
