https://github.com/vgvassilev updated https://github.com/llvm/llvm-project/pull/218149
>From bbf94c5e3a349c4eb8950484dfbf4a6d87d1b9b8 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 | 69 +++++++++ 4 files changed, 219 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 6941f0179647a6..2e6f689af6d970 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 12beb542572d73..e2cdeebbe8de7a 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 b626cebaafcd70..0f6ce4587219e4 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 00000000000000..c56752e2a97b9e --- /dev/null +++ b/clang/test/Interpreter/failed-input-keeps-redecls.cpp @@ -0,0 +1,69 @@ +// 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 + +// Namespace alias and 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 fa18321af06f5198f7ac24d12727be59e6a9658f 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 | 282 +++++++----------- clang/lib/Interpreter/IncrementalParser.h | 21 -- .../failed-input-keeps-redecls.cpp | 33 ++ 5 files changed, 150 insertions(+), 189 deletions(-) diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 2e6f689af6d970..1ebc8a7191bde9 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 35911ee2f7d163..fe2648aa58301a 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 e2cdeebbe8de7a..3cfabb5e5daa18 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -17,11 +17,8 @@ #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/AST/DeclVisitor.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/Parse/Parser.h" @@ -197,188 +194,139 @@ 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> { + Sema &S; + TranslationUnitDecl *DiscardedTU; -template <typename DeclT> -void IncrementalParser::unlinkRedeclChain(Redeclarable<DeclT> *DBase, - NamedDecl *PrevND) { - auto *Latest = static_cast<DeclT *>(DBase); - auto *Survivor = cast<DeclT>(PrevND); + template <typename DeclT> void withdraw(Redeclarable<DeclT> *DBase) { + if (NamedDecl *Prev = findSurvivor(static_cast<DeclT *>(DBase))) + unlinkRedeclChain(S.getASTContext(), DBase, Prev); + } - // Rebuild First -> ... -> Survivor -> ... -> Latest as - // First -> ... -> Survivor. - Latest->getFirstDecl()->RedeclLink.setLatest(Survivor); + /// 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; + } - // 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 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; + } } -} -template <typename DeclT> -void IncrementalParser::withdrawRedeclImpl(Redeclarable<DeclT> *D, - NamedDecl *Prev, - TranslationUnitDecl *) { - unlinkRedeclChain(D, Prev); -} + /// Remove entry from "C"'s lookup tables + void removeFromLookups(NamedDecl *D) { + if (D->getDeclName().isEmpty()) + return; -template <> -void IncrementalParser::withdrawRedeclImpl(Redeclarable<TagDecl> *D, - NamedDecl *Prev, - TranslationUnitDecl *DiscardedTU) { - unlinkRedeclChain(D, Prev); + if (D->getDeclName().isIdentifier() && D->getDeclName().getFETokenInfo() && + !D->getLangOpts().ObjC && !D->getLangOpts().CPlusPlus) + S.IdResolver.RemoveDecl(D); - // 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; -} + ExternCContextDecl *ECCD = S.getASTContext().getExternCContextDecl(); + if (StoredDeclsMap *Map = ECCD->getPrimaryContext()->getLookupPtr()) { + auto It = Map->find(D->getDeclName()); + if (It != Map->end()) + It->second.remove(D); + } + } -template <> -void IncrementalParser::withdrawRedeclImpl(Redeclarable<NamespaceDecl> *D, - NamedDecl *Prev, - TranslationUnitDecl *) { - dropContainingMembers(static_cast<NamespaceDecl *>(D)); - unlinkRedeclChain(D, Prev); -} + /// Remove Decls defined in this DC from the lookup table + /// and restore the redeclaration chain to previous state + void VisitDeclContext(DeclContext *DC) { + llvm::SmallVector<Decl *, 8> Members(DC->decls()); + llvm::SmallVector<NamedDecl *, 8> Survivors; + for (Decl *M : Members) { + if (auto *ND = dyn_cast<NamedDecl>(M)) + if (NamedDecl *Prev = findSurvivor(ND)) + Survivors.push_back(Prev); + Visit(M); // restore redecls + DC->removeDecl(M); // remove from lookup + if (auto *ND = dyn_cast<NamedDecl>(M)) + removeFromLookups(ND); + } -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); -} + // Restore lookup for the surviving predecessor + // of any removed decl that had a surviving predecessor + DeclContext *Primary = DC->getPrimaryContext(); + for (NamedDecl *Prev : Survivors) + Primary->makeDeclVisibleInContext(Prev); + } -void IncrementalParser::withdrawRedeclImpl(...) { - llvm_unreachable("withdrawRedecl on a non-redeclarable declaration"); -} +public: + ASTDeclUnmerger(Sema &S, TranslationUnitDecl *DiscardedTU) + : S(S), DiscardedTU(DiscardedTU) {} -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 VisitDecl(Decl *D) { + if (auto *DC = dyn_cast<DeclContext>(D)) + VisitDeclContext(DC); } -} -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) { - 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); - } - } - for (DeclarationName Key : KeysToErase) - Map->erase(Key); - for (auto &[Key, Prev] : DeclsToRestore) - (*Map)[Key].addOrReplaceDecl(Prev); + 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(S.getASTContext(), D, Prev); + + // A class definition is 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; } - // Check if we need to clean up the IdResolver chain. - auto RemoveFromIdResolver = [&](NamedDecl *D) { - if (D->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC && - !D->getLangOpts().CPlusPlus) - S.IdResolver.RemoveDecl(D); - }; - - ExternCContextDecl *ECCD = S.getASTContext().getExternCContextDecl(); - if (StoredDeclsMap *Map = ECCD->getPrimaryContext()->getLookupPtr()) { - for (auto &&[Key, List] : *Map) { - DeclContextLookupResult R = List.getLookupResult(); - llvm::SmallVector<NamedDecl *, 4> NamedDeclsToRemove; - for (NamedDecl *D : R) { - // Implicitly generated C decl is not attached to the current TU but - // lexically attached to the recent TU, so we need to check the lexical - // context. - DeclContext *LDC = D->getLexicalDeclContext(); - while (LDC && !isa<TranslationUnitDecl>(LDC)) - LDC = LDC->getLexicalParent(); - TranslationUnitDecl *TopTU = cast_or_null<TranslationUnitDecl>(LDC); - if (TopTU == MostRecentTU) - NamedDeclsToRemove.push_back(D); - } - for (NamedDecl *D : NamedDeclsToRemove) { - List.remove(D); - RemoveFromIdResolver(D); - } - } + void VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { + withdraw(D); + Visit(D->getTemplatedDecl()); } - for (Decl *D : MostRecentTU->decls()) { - auto *ND = dyn_cast<NamedDecl>(D); - if (!ND || ND->getDeclName().isEmpty()) - continue; - RemoveFromIdResolver(ND); + 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 + VisitDeclContext(D); + withdraw(D); } + void VisitTranslationUnitDecl(TranslationUnitDecl *D) { VisitDeclContext(D); } +}; + +void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) { + ASTDeclUnmerger(S, MostRecentTU).Visit(MostRecentTU); + // Lookup alone is not enough: the redeclaration chain still reaches these. withdrawMostRecentTU(MostRecentTU); } diff --git a/clang/lib/Interpreter/IncrementalParser.h b/clang/lib/Interpreter/IncrementalParser.h index 0f6ce4587219e4..b626cebaafcd70 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 c56752e2a97b9e..1e582220ea8d79 100644 --- a/clang/test/Interpreter/failed-input-keeps-redecls.cpp +++ b/clang/test/Interpreter/failed-input-keeps-redecls.cpp @@ -66,4 +66,37 @@ using Deep::g; int e9 = undeclared_thing; g(); // CHECK-DAG: Deep::g +// A member of a re-opened namespace is a redeclaration on its own. +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 + +// Anonymous namespace +namespace { int anon_v = 11; } int e13 = undeclared_thing; +// CHECK-DAG: error: use of undeclared identifier 'undeclared_thing' +namespace { int anon_v = 22; } +printf("anon_v = %d\n", anon_v); +// CHECK-DAG: anon_v = 22 + %quit _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
