https://github.com/guitargeek updated https://github.com/llvm/llvm-project/pull/229675
>From 18c869355a375214a3458fa47a862268d72ea0a1 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 | 20 +++++++++++---- .../Interpreter/value-print-temporaries.cpp | 25 +++++++++++++++++++ 2 files changed, 40 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..bb89e1d885bda 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,17 @@ 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
