[clang] [clang-repl] Evaluate a void expression that has no trailing semicolon (PR #228334)
Jonas Rembser via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 1 22:50:50 PDT 2026
https://github.com/guitargeek created https://github.com/llvm/llvm-project/pull/228334
An expression statement without a semicolon at the end of an input is replaced by a call to `__clang_Interpreter_SetValueNoAlloc` that captures its value. For an expression of type `void`, the expression was not an argument of that call and was dropped, so `f()` did not call `f`.
Build `(E, __clang_Interpreter_SetValueNoAlloc(...))` instead.
Fixes #219800.
Assisted-by: Claude Opus 5.5
>From 4346f221cd3c60b27e5fa9ebac1a0b77465c90c7 Mon Sep 17 00:00:00 2001
From: Jonas Rembser <jonas.rembser at cern.ch>
Date: Wed, 30 Sep 2026 16:53:50 +0000
Subject: [PATCH] [clang-repl] Evaluate a void expression that has no trailing
semicolon
An expression statement without a semicolon at the end of an input is
replaced by a call to `__clang_Interpreter_SetValueNoAlloc` that captures
its value. For an expression of type `void`, the expression was not an
argument of that call and was dropped, so `f()` did not call `f`.
Build `(E, __clang_Interpreter_SetValueNoAlloc(...))` instead.
Fixes #219800.
Assisted-by: Claude Opus 5.5
---
clang/lib/Interpreter/InterpreterValuePrinter.cpp | 10 +++++++---
clang/test/Interpreter/pretty-print.cpp | 11 +++++++++++
clang/unittests/Interpreter/InterpreterTest.cpp | 15 +++++++++++++++
3 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Interpreter/InterpreterValuePrinter.cpp b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
index 79f1e2b6571c6..6842656e87f44 100644
--- a/clang/lib/Interpreter/InterpreterValuePrinter.cpp
+++ b/clang/lib/Interpreter/InterpreterValuePrinter.cpp
@@ -424,9 +424,8 @@ class InterfaceKindVisitor
Args.push_back(E);
else if (Ty->isIntegralOrEnumerationType())
HandleIntegralOrEnumType(Ty);
- else if (Ty->isVoidType()) {
- // Do we need to still run `E`?
- }
+ // A void expression is not an argument of the call; convertExprToValue
+ // evaluates it before the call instead.
return InterfaceKind::NoAlloc;
}
@@ -618,6 +617,11 @@ llvm::Expected<Expr *> Interpreter::convertExprToValue(Expr *E) {
SetValueE =
S.ActOnCallExpr(Scope, ValuePrintingInfo[InterfaceKind::NoAlloc],
E->getBeginLoc(), AdjustedArgs, E->getEndLoc());
+ // A void expression must still be evaluated:
+ // `(E, __clang_Interpreter_SetValueNoAlloc(...))`.
+ if (DesugaredTy->isVoidType() && !SetValueE.isInvalid())
+ SetValueE =
+ S.CreateBuiltinBinOp(E->getEndLoc(), BO_Comma, E, SetValueE.get());
break;
}
default:
diff --git a/clang/test/Interpreter/pretty-print.cpp b/clang/test/Interpreter/pretty-print.cpp
index 204939eb3b523..ed430d2db6d22 100644
--- a/clang/test/Interpreter/pretty-print.cpp
+++ b/clang/test/Interpreter/pretty-print.cpp
@@ -89,6 +89,17 @@ const decltype(N::D()) decl3; decl3
__remove_extent(N::D)* decl4; decl4
// CHECK-NEXT: (N::D *)
+// A call to a void function without a semicolon must still be executed.
+// https://github.com/llvm/llvm-project/issues/219800
+int void_x = 10;
+void set_void_x() { void_x = 100; }
+set_void_x()
+void_x
+// CHECK-NEXT: (int) 100
+void print_foo() { printf("foo()\n"); }
+print_foo()
+// CHECK-NEXT: foo()
+
// int i = 12;
// int &iref = i;
// iref
diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp
index 3becd00c12820..a0ed7e671ddbe 100644
--- a/clang/unittests/Interpreter/InterpreterTest.cpp
+++ b/clang/unittests/Interpreter/InterpreterTest.cpp
@@ -469,6 +469,21 @@ TEST_F(InterpreterTest, Value) {
EXPECT_STREQ(prettyPrint.c_str(), "(D) (One) : unsigned int 1\n");
}
+TEST_F(InterpreterTest, ValueOfVoidCallExecutesTheCall) {
+ std::unique_ptr<Interpreter> Interp = createInterpreter();
+
+ llvm::cantFail(
+ Interp->ParseAndExecute("int calls = 0; void bump() { ++calls; }"));
+ Value V;
+ llvm::cantFail(Interp->ParseAndExecute("bump()", &V));
+ EXPECT_TRUE(V.isValid());
+ EXPECT_EQ(V.getKind(), Value::K_Void);
+
+ Value Calls;
+ llvm::cantFail(Interp->ParseAndExecute("calls", &Calls));
+ EXPECT_EQ(Calls.getInt(), 1);
+}
+
// Regression: Value::setRawBits's NBytes parameter must be interpreted as a
// byte count end-to-end. Before this was fixed, the parameter was named
// NBits and the memcpy divided by 8, so a caller passing sizeof(T) (the
More information about the cfe-commits
mailing list