https://github.com/guitargeek created
https://github.com/llvm/llvm-project/pull/229675
convertExprToValue() strips the ExprWithCleanups of the printed expression and
builds the call of __clang_Interpreter_SetValue* around the subexpression,
without the cleanups. The temporaries of the expression then belong to no
full-expression: CodeGen destroys them at the end of the function that runs the
top-level statements, which it finishes after it has emitted the deferred
declarations, so their destructors are referenced but never emitted:
clang-repl> struct S { ~S() {} };
clang-repl> int f(S) { return 42; }
clang-repl> f(S())
JIT session error: Symbols not found: [ _ZN1SD1Ev ]
(with an assertions build, moveLazyEmissionStates() asserts instead).
Put the cleanups back around the result, so that the temporaries are destroyed
at the end of their statement. If the call cannot be built, return the original
expression with its cleanups.
Assisted-by: Claude Opus 5.5
>From a448f25342c54e1ad79c5282ac567a41a7371074 Mon Sep 17 00:00:00 2001
From: Jonas Rembser <[email protected]>
Date: Tue, 6 Oct 2026 21:29:26 +0000
Subject: [PATCH] [clang-repl] Keep the cleanups of value-printed expressions
convertExprToValue() strips the ExprWithCleanups of the printed
expression and builds the call of __clang_Interpreter_SetValue* around
the subexpression, without the cleanups. The temporaries of the
expression then belong to no full-expression: CodeGen destroys them at
the end of the function that runs the top-level statements, which it
finishes after it has emitted the deferred declarations, so their
destructors are referenced but never emitted:
clang-repl> struct S { ~S() {} };
clang-repl> int f(S) { return 42; }
clang-repl> f(S())
JIT session error: Symbols not found: [ _ZN1SD1Ev ]
(with an assertions build, moveLazyEmissionStates() asserts instead).
Put the cleanups back around the result, so that the temporaries are
destroyed at the end of their statement. If the call cannot be built,
return the original expression with its cleanups.
Assisted-by: Claude Opus 5.5
---
.../Interpreter/InterpreterValuePrinter.cpp | 21 ++++++++++++----
.../Interpreter/value-print-temporaries.cpp | 25 +++++++++++++++++++
2 files changed, 41 insertions(+), 5 deletions(-)
create mode 100644 clang/test/Interpreter/value-print-temporaries.cpp
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 6842656e87f44..666d078ccd83e 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -528,8 +528,10 @@ llvm::Expected<Expr *>
Interpreter::convertExprToValue(Expr *E) {
// Build `__clang_Interpreter_SetValue*` call.
- // Get rid of ExprWithCleanups.
- if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
+ // Get rid of ExprWithCleanups; it is put back around the result below.
+ Expr *FullExpr = E;
+ auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E);
+ if (EWC)
E = EWC->getSubExpr();
QualType Ty = E->IgnoreImpCasts()->getType();
@@ -630,9 +632,18 @@ llvm::Expected<Expr *>
Interpreter::convertExprToValue(Expr *E) {
// It could fail, like printing an array type in C. (not supported)
if (SetValueE.isInvalid())
- return E;
-
- return SetValueE.get();
+ return FullExpr;
+
+ // The temporaries of E must be destroyed at the end of the statement.
+ // Without the cleanups, CodeGen destroys them at the end of the function
+ // running the top-level statements, which it finishes after emitting the
+ // deferred declarations: their destructors would not be emitted.
+ Expr *Result = SetValueE.get();
+ if (EWC && !isa<ExprWithCleanups>(Result))
+ Result = ExprWithCleanups::Create(Ctx, Result,
+ EWC->cleanupsHaveSideEffects(),
+ EWC->getObjects());
+ return Result;
}
} // namespace clang
diff --git a/clang/test/Interpreter/value-print-temporaries.cpp
b/clang/test/Interpreter/value-print-temporaries.cpp
new file mode 100644
index 0000000000000..e826636a4a30c
--- /dev/null
+++ b/clang/test/Interpreter/value-print-temporaries.cpp
@@ -0,0 +1,25 @@
+// The temporaries of a printed expression are destroyed at the end of its
+// statement, and their destructors are emitted.
+//
+// RUN: cat %s | clang-repl | FileCheck %s
+
+int Dtors = 0;
+struct S { ~S() { ++Dtors; } };
+int f(S) { return 42; }
+
+f(S())
+// CHECK: (int) 42
+
+Dtors
+// CHECK-NEXT: (int) 1
+
+struct R { int I; ~R() { ++Dtors; } };
+R g(const S &) { return R{7}; }
+
+g(S()).I
+// CHECK-NEXT: (int) 7
+
+Dtors
+// CHECK-NEXT: (int) 3
+
+%quit
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits