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 ff2223070dc157..3673738408e554 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..ff609f3255b036 --- /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 0342f50f2b0f496ebcf26e190672e899cdaecfd7 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/lib/Interpreter/IncrementalParser.cpp | 187 +++++++++--------- clang/lib/Interpreter/IncrementalParser.h | 23 +-- .../failed-input-keeps-redecls.cpp | 28 +++ 3 files changed, 127 insertions(+), 111 deletions(-) diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp index e2cdeebbe8de7a..ea25e2455a1372 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -22,6 +22,7 @@ #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 +198,108 @@ 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); -} +class IncrementalParser::DeclWithdrawer + : public DeclVisitor<IncrementalParser::DeclWithdrawer> { + 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; -} + DeclWithdrawer(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) { + DeclWithdrawer Withdrawer(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 +322,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 = Withdrawer.findSurvivor(D); + Withdrawer.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 0f6ce4587219e4..05cc051a9ee6ca 100644 --- a/clang/lib/Interpreter/IncrementalParser.h +++ b/clang/lib/Interpreter/IncrementalParser.h @@ -25,6 +25,8 @@ class ASTConsumer; class CompilerInstance; class Parser; class Sema; +class ASTContext; +class CXXRecordDecl; class NamedDecl; class TranslationUnitDecl; template <typename decl_type> class Redeclarable; @@ -76,24 +78,9 @@ class IncrementalParser { /// 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(...); + /// Removes decls introduced in the discarding PTU and restores the + /// redeclaration chain to previous state. + class DeclWithdrawer; }; } // end namespace clang diff --git a/clang/test/Interpreter/failed-input-keeps-redecls.cpp b/clang/test/Interpreter/failed-input-keeps-redecls.cpp index ff609f3255b036..d7fd37fa7c2f61 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
