Author: Vipul Cariappa Date: 2026-08-23T10:22:31+03:00 New Revision: ec5c9182be2aa168b7c843374c5c7497dd3bef17
URL: https://github.com/llvm/llvm-project/commit/ec5c9182be2aa168b7c843374c5c7497dd3bef17 DIFF: https://github.com/llvm/llvm-project/commit/ec5c9182be2aa168b7c843374c5c7497dd3bef17.diff LOG: [clang-repl] Don't double-remove extern "C" decls from the IdResolver (#218129) Regression was introduced in https://github.com/llvm/llvm-project/pull/178648 This is required for the 23 release; the downstream tools CppInterOp & Cppyy will fail otherwise. Added: clang/test/Interpreter/extern-c-error-recovery.cpp Modified: clang/lib/Interpreter/IncrementalParser.cpp Removed: ################################################################################ diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp index 59018907056b8..12beb542572d7 100644 --- a/clang/lib/Interpreter/IncrementalParser.cpp +++ b/clang/lib/Interpreter/IncrementalParser.cpp @@ -217,6 +217,13 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) { Map->erase(Key); } + // 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) { @@ -235,7 +242,7 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) { } for (NamedDecl *D : NamedDeclsToRemove) { List.remove(D); - S.IdResolver.RemoveDecl(D); + RemoveFromIdResolver(D); } } } @@ -244,10 +251,7 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) { auto *ND = dyn_cast<NamedDecl>(D); if (!ND || ND->getDeclName().isEmpty()) continue; - // Check if we need to clean up the IdResolver chain. - if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC && - !D->getLangOpts().CPlusPlus) - S.IdResolver.RemoveDecl(ND); + RemoveFromIdResolver(ND); } // Lookup alone is not enough: the redeclaration chain still reaches these. diff --git a/clang/test/Interpreter/extern-c-error-recovery.cpp b/clang/test/Interpreter/extern-c-error-recovery.cpp new file mode 100644 index 0000000000000..20b5223700007 --- /dev/null +++ b/clang/test/Interpreter/extern-c-error-recovery.cpp @@ -0,0 +1,63 @@ +// REQUIRES: host-supports-jit +// RUN: cat %s | clang-repl > %t.out 2>&1 +// RUN: FileCheck %s --input-file=%t.out +// RUN: FileCheck %s --check-prefix=NEGATIVE --input-file=%t.out + +// An input that declares something with C language linkage and then fails must +// not take the interpreter down with it. + +extern "C" int printf(const char *, ...); + +// An error in the body of an extern "C" function definition. +extern "C" void f1() { undeclared_thing; } +// CHECK-DAG: error: use of undeclared identifier 'undeclared_thing' +printf("alive %d\n", 1); +// CHECK-DAG: alive 1 + +// The same, written as an `extern "C" { ... }` block. +extern "C" { void f2() { undeclared_thing; } } +printf("alive %d\n", 2); +// CHECK-DAG: alive 2 + +// An extern "C" *variable* whose initializer fails: variables are registered +// with the ExternCContext by a diff erent Sema path than functions. +extern "C" int v1 = undeclared_thing; +printf("alive %d\n", 3); +// CHECK-DAG: alive 3 + +// A deleted destructor reached through a wrapper -- the shape CppInterOp's +// generated destructor wrappers hit. +class D { public: ~D() = delete; }; +extern "C" void g(D *p) { delete p; } +// CHECK-DAG: error: attempt to use a deleted function +printf("alive %d\n", 4); +// CHECK-DAG: alive 4 + +// A block-scope `extern` inside an extern "C" function is registered with the +// ExternCContext but is already off the IdResolver by the time CleanUpPTU runs, +// because its own scope popped while the input was still being parsed. Removing +// it again therefore does not just fail to find it: with assertions off it +// clears the identifier's chain out from under whatever else is on it. +extern "C" void h1() { extern int fresh; undeclared_thing; } int fresh = 1; +printf("alive %d\n", 5); +// CHECK-DAG: alive 5 + +// Surviving is not enough: the discarded PTU must leave nothing behind, so the +// very same names have to be definable afterwards and the definitions have to +// be the ones that run. +extern "C" void f1() { printf("f1 ran\n"); } +extern "C" void f2() { printf("f2 ran\n"); } +extern "C" int v1 = 5; +f1(); +f2(); +printf("v1 %d\n", v1); +// CHECK-DAG: f1 ran +// CHECK-DAG: f2 ran +// CHECK-DAG: v1 5 + +// Nothing anywhere in the session may claim the recovered definitions clash +// with what the discarded inputs left behind. +// NEGATIVE-NOT: error: redefinition +// NEGATIVE-NOT: error: conflicting types + +%quit _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
