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 <[email protected]> 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
