[llvm-branch-commits] [clang] release/23.x: [clang-repl] Don't double-remove extern "C" decls from the IdResolver (#218129) (PR #218220)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Aug 23 01:48:53 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: llvmbot
<details>
<summary>Changes</summary>
Backport ec5c9182be2aa168b7c843374c5c7497dd3bef17
Requested by: @<!-- -->Vipul-Cariappa
---
Full diff: https://github.com/llvm/llvm-project/pull/218220.diff
2 Files Affected:
- (modified) clang/lib/Interpreter/IncrementalParser.cpp (+9-5)
- (added) clang/test/Interpreter/extern-c-error-recovery.cpp (+63)
``````````diff
diff --git a/clang/lib/Interpreter/IncrementalParser.cpp b/clang/lib/Interpreter/IncrementalParser.cpp
index f6d2779d64b2b..0f28eed888a05 100644
--- a/clang/lib/Interpreter/IncrementalParser.cpp
+++ b/clang/lib/Interpreter/IncrementalParser.cpp
@@ -198,6 +198,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) {
@@ -216,7 +223,7 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
}
for (NamedDecl *D : NamedDeclsToRemove) {
List.remove(D);
- S.IdResolver.RemoveDecl(D);
+ RemoveFromIdResolver(D);
}
}
}
@@ -226,10 +233,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);
}
}
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 different 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
``````````
</details>
https://github.com/llvm/llvm-project/pull/218220
More information about the llvm-branch-commits
mailing list