[libc-commits] [libc] [llvm] [libc] Use cpp::function in death test executors and FPExceptMatcher (PR #235777)
Pavel Labath via libc-commits
libc-commits at lists.llvm.org
Mon Oct 26 06:51:35 PDT 2026
https://github.com/labath created https://github.com/llvm/llvm-project/pull/235777
Instead of the hand-rolled FunctionCaller classes, which had to be heap-allocated and then manually deleted (on every exit path in the case of invoke_in_subprocess), pass the callable as a cpp::function. This lets us remove the createCallable/getFunctionCaller helpers and all the `delete func` statements.
cpp::function does not own the callable, but that's fine here as the callable (a temporary created by the test macro) outlives the call.
Assisted-by: Gemini
>From 8bf672799fd6faa164c2379f04a8e8a953965c98 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Mon, 26 Oct 2026 13:50:21 +0000
Subject: [PATCH] [libc] Use cpp::function in death test executors and
FPExceptMatcher
Instead of the hand-rolled FunctionCaller classes, which had to be
heap-allocated and then manually deleted (on every exit path in the
case of invoke_in_subprocess), pass the callable as a cpp::function.
This lets us remove the createCallable/getFunctionCaller helpers and all
the `delete func` statements.
cpp::function does not own the callable, but that's fine here as the
callable (a temporary created by the test macro) outlives the call.
Assisted-by: Gemini
---
libc/test/UnitTest/CMakeLists.txt | 4 +++
libc/test/UnitTest/ExecuteFunction.h | 9 ++-----
libc/test/UnitTest/ExecuteFunctionUnix.cpp | 22 ++++++----------
libc/test/UnitTest/FPExceptMatcher.cpp | 6 ++---
libc/test/UnitTest/FPExceptMatcher.h | 25 +++----------------
libc/test/UnitTest/LibcDeathTestExecutors.cpp | 4 +--
libc/test/UnitTest/LibcTest.h | 19 +++-----------
.../libc/test/UnitTest/BUILD.bazel | 2 ++
8 files changed, 26 insertions(+), 65 deletions(-)
diff --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt
index c99666dde0eaf5..de82c2f1362d65 100644
--- a/libc/test/UnitTest/CMakeLists.txt
+++ b/libc/test/UnitTest/CMakeLists.txt
@@ -102,6 +102,7 @@ add_unittest_framework_library(
libc.hdr.stdint_proxy
libc.src.__support.big_int
libc.src.__support.c_string
+ libc.src.__support.CPP.functional
libc.src.__support.CPP.string
libc.src.__support.CPP.string_view
libc.src.__support.CPP.type_traits
@@ -124,6 +125,7 @@ add_unittest_framework_library(
if(LIBC_TEST_SUBPROCESS_TESTS)
set(libc_death_test_deps
libc.hdr.stdint_proxy
+ libc.src.__support.CPP.functional
libc.src.__support.CPP.scope
libc.src.__support.libc_assert
)
@@ -158,6 +160,7 @@ else()
ExecuteFunction.h
DEPENDS
libc.hdr.stdint_proxy
+ libc.src.__support.CPP.functional
)
endif()
@@ -229,6 +232,7 @@ add_unittest_framework_library(
FPExceptMatcher.h
DEPENDS
.LibcTest
+ libc.src.__support.CPP.functional
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.fenv_impl
libc.hdr.types.fenv_t
diff --git a/libc/test/UnitTest/ExecuteFunction.h b/libc/test/UnitTest/ExecuteFunction.h
index 5844fd55d4e286..4c9dc1387429dd 100644
--- a/libc/test/UnitTest/ExecuteFunction.h
+++ b/libc/test/UnitTest/ExecuteFunction.h
@@ -10,18 +10,13 @@
#define LLVM_LIBC_TEST_UNITTEST_EXECUTEFUNCTION_H
#include "hdr/stdint_proxy.h"
+#include "src/__support/CPP/functional.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
namespace testutils {
-class FunctionCaller {
-public:
- virtual ~FunctionCaller() {}
- virtual void operator()() = 0;
-};
-
struct ProcessStatus {
int platform_defined;
const char *failure = nullptr;
@@ -42,7 +37,7 @@ struct ProcessStatus {
int get_fatal_signal();
};
-ProcessStatus invoke_in_subprocess(FunctionCaller *func,
+ProcessStatus invoke_in_subprocess(cpp::function<void(void)> func,
int timeout_ms = ProcessStatus::TIMEOUT);
const char *signal_as_string(int signum);
diff --git a/libc/test/UnitTest/ExecuteFunctionUnix.cpp b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
index 919ef0536034c7..8ef03da219bdb9 100644
--- a/libc/test/UnitTest/ExecuteFunctionUnix.cpp
+++ b/libc/test/UnitTest/ExecuteFunctionUnix.cpp
@@ -66,29 +66,25 @@ int ProcessStatus::get_fatal_signal() {
return WTERMSIG(platform_defined);
}
-ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
+ProcessStatus invoke_in_subprocess(cpp::function<void(void)> func,
+ int timeout_ms) {
int pipe_fds[2];
- if (LIBC_IMPL::pipe(pipe_fds) == -1) {
- delete func;
+ if (LIBC_IMPL::pipe(pipe_fds) == -1)
return ProcessStatus::error("pipe(2) failed");
- }
// Don't copy the buffers into the child process and print twice.
LIBC_IMPL::fflush(stderr);
LIBC_IMPL::fflush(stdout);
pid_t pid = LIBC_IMPL::fork();
- if (pid == -1) {
- delete func;
+ if (pid == -1)
return ProcessStatus::error("fork(2) failed");
- }
if (!pid) {
LIBC_IMPL::close(pipe_fds[0]);
#if defined(__linux__) && defined(PR_SET_DUMPABLE)
LIBC_IMPL::prctl(PR_SET_DUMPABLE, 0);
#endif
- (*func)();
- delete func;
+ func();
LIBC_IMPL::exit(0);
}
LIBC_IMPL::close(pipe_fds[1]);
@@ -97,14 +93,12 @@ ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
pollfd poll_fd{pipe_fds[0], POLLIN, 0};
// No events requested so this call will only return after the timeout or if
// the pipes peer was closed, signaling the process exited.
- if (LIBC_IMPL::poll(&poll_fd, 1, timeout_ms) == -1) {
- delete func;
+ if (LIBC_IMPL::poll(&poll_fd, 1, timeout_ms) == -1)
return ProcessStatus::error("poll(2) failed");
- }
+
// If the pipe wasn't closed by the child yet then timeout has expired.
if (!(poll_fd.revents & POLLHUP)) {
LIBC_IMPL::kill(pid, SIGKILL);
- delete func;
return ProcessStatus::timed_out_ps();
}
@@ -113,11 +107,9 @@ ProcessStatus invoke_in_subprocess(FunctionCaller *func, int timeout_ms) {
// and doesn't turn into a zombie.
pid_t status = LIBC_IMPL::waitpid(pid, &wstatus, 0);
if (status == -1) {
- delete func;
return ProcessStatus::error("waitpid(2) failed");
}
LIBC_ASSERT(status == pid);
- delete func;
return {wstatus};
}
diff --git a/libc/test/UnitTest/FPExceptMatcher.cpp b/libc/test/UnitTest/FPExceptMatcher.cpp
index 6da21a279ff3de..9b2d909eec145a 100644
--- a/libc/test/UnitTest/FPExceptMatcher.cpp
+++ b/libc/test/UnitTest/FPExceptMatcher.cpp
@@ -14,7 +14,6 @@
#include "FPExceptMatcher.h"
#include "src/__support/macros/config.h"
-#include "test/UnitTest/ExecuteFunction.h" // FunctionCaller
#include "test/UnitTest/Test.h"
#include "hdr/types/fenv_t.h"
@@ -48,7 +47,7 @@ static void sigfpeHandler([[maybe_unused]] int sig) {
#endif // TARGET_SUPPORTS_SIGNAL_CATCHING
-FPExceptMatcher::FPExceptMatcher(FunctionCaller *func) {
+FPExceptMatcher::FPExceptMatcher(cpp::function<void(void)> func) {
#if TARGET_SUPPORTS_SIGNAL_CATCHING
auto *oldSIGFPEHandler = signal(SIGFPE, &sigfpeHandler);
#endif
@@ -59,8 +58,7 @@ FPExceptMatcher::FPExceptMatcher(FunctionCaller *func) {
#if TARGET_SUPPORTS_SIGNAL_CATCHING
if (sigsetjmp(jumpBuffer, 1) == 0)
#endif
- func->call();
- delete func;
+ func();
// We restore the previous floating point environment after
// the call to the function which can potentially raise SIGFPE.
fputil::set_env(&oldEnv);
diff --git a/libc/test/UnitTest/FPExceptMatcher.h b/libc/test/UnitTest/FPExceptMatcher.h
index 9b05e83bc1e3aa..421ece17978b18 100644
--- a/libc/test/UnitTest/FPExceptMatcher.h
+++ b/libc/test/UnitTest/FPExceptMatcher.h
@@ -9,6 +9,7 @@
#ifndef LLVM_LIBC_TEST_UNITTEST_FPEXCEPTMATCHER_H
#define LLVM_LIBC_TEST_UNITTEST_FPEXCEPTMATCHER_H
+#include "src/__support/CPP/functional.h"
#include "src/__support/macros/config.h"
#include "test/UnitTest/Test.h"
#include "test/UnitTest/TestLogger.h"
@@ -24,24 +25,7 @@ class FPExceptMatcher : public Matcher<bool> {
bool exceptionRaised;
public:
- class FunctionCaller {
- public:
- virtual ~FunctionCaller() {}
- virtual void call() = 0;
- };
-
- template <typename Func> static FunctionCaller *getFunctionCaller(Func func) {
- struct Callable : public FunctionCaller {
- Func f;
- explicit Callable(Func theFunc) : f(theFunc) {}
- void call() override { f(); }
- };
-
- return new Callable(func);
- }
-
- // Takes ownership of func.
- explicit FPExceptMatcher(FunctionCaller *func);
+ explicit FPExceptMatcher(cpp::function<void(void)> func);
bool match([[maybe_unused]] bool unused) { return exceptionRaised; }
@@ -55,10 +39,7 @@ class FPExceptMatcher : public Matcher<bool> {
} // namespace LIBC_NAMESPACE_DECL
#define ASSERT_RAISES_FP_EXCEPT(func) \
- ASSERT_THAT( \
- true, \
- LIBC_NAMESPACE::testing::FPExceptMatcher( \
- LIBC_NAMESPACE::testing::FPExceptMatcher::getFunctionCaller(func)))
+ ASSERT_THAT(true, LIBC_NAMESPACE::testing::FPExceptMatcher(func))
#else // !LIBC_TEST_HAS_MATCHERS()
diff --git a/libc/test/UnitTest/LibcDeathTestExecutors.cpp b/libc/test/UnitTest/LibcDeathTestExecutors.cpp
index d579ae52e6383e..a50c0ea1ddadc0 100644
--- a/libc/test/UnitTest/LibcDeathTestExecutors.cpp
+++ b/libc/test/UnitTest/LibcDeathTestExecutors.cpp
@@ -20,7 +20,7 @@ constexpr unsigned TIMEOUT_MS = 10000;
namespace LIBC_NAMESPACE_DECL {
namespace testing {
-bool Test::testProcessKilled(testutils::FunctionCaller *Func, int Signal,
+bool Test::testProcessKilled(cpp::function<void(void)> Func, int Signal,
const char *LHSStr,
[[maybe_unused]] const char *RHSStr,
internal::Location Loc) {
@@ -65,7 +65,7 @@ bool Test::testProcessKilled(testutils::FunctionCaller *Func, int Signal,
return false;
}
-bool Test::testProcessExits(testutils::FunctionCaller *Func, int ExitCode,
+bool Test::testProcessExits(cpp::function<void(void)> Func, int ExitCode,
const char *LHSStr, const char *RHSStr,
internal::Location Loc) {
testutils::ProcessStatus Result =
diff --git a/libc/test/UnitTest/LibcTest.h b/libc/test/UnitTest/LibcTest.h
index be3574c21a7304..cd6ccecc7134ef 100644
--- a/libc/test/UnitTest/LibcTest.h
+++ b/libc/test/UnitTest/LibcTest.h
@@ -24,6 +24,7 @@
// This file can only include headers from src/__support/ or test/UnitTest. No
// other headers should be included.
+#include "src/__support/CPP/functional.h"
#include "src/__support/CPP/string.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/CPP/type_traits.h"
@@ -213,24 +214,14 @@ class Test {
return testMatch(Matcher.match(Value), Matcher, ValueStr, MatcherStr, Loc);
}
- bool testProcessExits(testutils::FunctionCaller *Func, int ExitCode,
+ bool testProcessExits(cpp::function<void(void)> Func, int ExitCode,
const char *LHSStr, const char *RHSStr,
internal::Location Loc);
- bool testProcessKilled(testutils::FunctionCaller *Func, int Signal,
+ bool testProcessKilled(cpp::function<void(void)> Func, int Signal,
const char *LHSStr, const char *RHSStr,
internal::Location Loc);
- template <typename Func> testutils::FunctionCaller *createCallable(Func f) {
- struct Callable : public testutils::FunctionCaller {
- Func f;
- Callable(Func f) : f(f) {}
- void operator()() override { f(); }
- };
-
- return new Callable(f);
- }
-
private:
virtual void Run() = 0;
virtual const char *getName() const = 0;
@@ -502,9 +493,7 @@ CString libc_make_test_file_path_func(const char *file_name);
#define LIBC_TEST_PROCESS_(TEST_FUNC, FUNC, VALUE, RET_OR_EMPTY) \
LIBC_TEST_SCAFFOLDING_( \
- TEST_FUNC(LIBC_NAMESPACE::testing::Test::createCallable(FUNC), VALUE, \
- #FUNC, #VALUE, LIBC_TEST_LOC_()), \
- RET_OR_EMPTY)
+ TEST_FUNC(FUNC, VALUE, #FUNC, #VALUE, LIBC_TEST_LOC_()), RET_OR_EMPTY)
#define EXPECT_EXITS(FUNC, EXIT) \
LIBC_TEST_PROCESS_(testProcessExits, FUNC, EXIT, )
diff --git a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
index d594c846957ceb..70c48473105a6b 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
@@ -53,6 +53,7 @@ libc_test_library(
"//libc:__support_c_string",
"//libc:__support_cpp_bit",
"//libc:__support_cpp_bitset",
+ "//libc:__support_cpp_functional",
"//libc:__support_cpp_limits",
"//libc:__support_cpp_scope",
"//libc:__support_cpp_span",
@@ -166,6 +167,7 @@ libc_test_library(
"//libc:__support_cpp_array",
"//libc:__support_cpp_bit",
"//libc:__support_cpp_bitset",
+ "//libc:__support_cpp_functional",
"//libc:__support_cpp_span",
"//libc:__support_cpp_type_traits",
"//libc:__support_cpp_utility",
More information about the libc-commits
mailing list