[llvm] [orc-rt] Fix move_only_function storing references to lvalue callables. (PR #226474)
Lang Hames via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 25 05:58:02 PDT 2026
https://github.com/lhames created https://github.com/llvm/llvm-project/pull/226474
move_only_function's converting constructor instantiated CallableImpl with the deduced forwarding-reference type, so constructing from an lvalue callable stored a reference to it rather than a copy, leaving the move_only_function dangling once the original went out of scope.
This commit decays the callable type before instantiating CallableImpl, and adds a static_assert to CallableImpl to enforce that it's only instantiated with decayed types.
>From e38684052d60044d04360fcba2222944d8207d58 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Fri, 25 Sep 2026 22:47:03 +1000
Subject: [PATCH] [orc-rt] Fix move_only_function storing references to lvalue
callables.
move_only_function's converting constructor instantiated CallableImpl with
the deduced forwarding-reference type, so constructing from an lvalue
callable stored a reference to it rather than a copy, leaving the
move_only_function dangling once the original went out of scope.
This commit decays the callable type before instantiating CallableImpl, and
adds a static_assert to CallableImpl to enforce that it's only instantiated
with decayed types.
---
.../orc-rt/support/move_only_function.h | 8 +++++++-
.../unit/support/move_only_function-test.cpp | 20 +++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/orc-rt/include/orc-rt/support/move_only_function.h b/orc-rt/include/orc-rt/support/move_only_function.h
index 22dbb529046aa8..52dabc81e18ed8 100644
--- a/orc-rt/include/orc-rt/support/move_only_function.h
+++ b/orc-rt/include/orc-rt/support/move_only_function.h
@@ -67,6 +67,9 @@ class CallableImpl;
template <typename CallableT, bool IsNoexcept, typename RetT, typename... ArgTs>
class CallableImpl<CallableT, false, IsNoexcept, RetT, ArgTs...>
: public Callable<false, IsNoexcept, RetT, ArgTs...> {
+
+ static_assert(std::is_same_v<CallableT, std::decay_t<CallableT>>);
+
public:
template <typename CallableInitT>
CallableImpl(CallableInitT &&Callable)
@@ -88,6 +91,9 @@ class CallableImpl<CallableT, false, IsNoexcept, RetT, ArgTs...>
template <typename CallableT, bool IsNoexcept, typename RetT, typename... ArgTs>
class CallableImpl<CallableT, true, IsNoexcept, RetT, ArgTs...>
: public Callable<true, IsNoexcept, RetT, ArgTs...> {
+
+ static_assert(std::is_same_v<CallableT, std::decay_t<CallableT>>);
+
public:
template <typename CallableInitT>
CallableImpl(CallableInitT &&Callable)
@@ -182,7 +188,7 @@ class move_only_function : public move_only_function_detail::MOFBase<FnT> {
template <typename CallableT> move_only_function(CallableT &&C) {
using WrappedCallable = typename move_only_function_detail::MOFBase<
- FnT>::template WrappedCallable<CallableT>;
+ FnT>::template WrappedCallable<std::decay_t<CallableT>>;
this->C = std::make_unique<WrappedCallable>(std::forward<CallableT>(C));
}
};
diff --git a/orc-rt/test/unit/support/move_only_function-test.cpp b/orc-rt/test/unit/support/move_only_function-test.cpp
index 1b0a79ecaca53e..73e317586e195a 100644
--- a/orc-rt/test/unit/support/move_only_function-test.cpp
+++ b/orc-rt/test/unit/support/move_only_function-test.cpp
@@ -246,6 +246,26 @@ TEST(MoveOnlyFunctionTest, ShouldCopyInitialize) {
EXPECT_FALSE(DidMove);
}
+TEST(MoveOnlyFunctionTest, LValueCallableIsCopiedNotReferenced) {
+ // Constructing from an lvalue callable must store a copy of it, not a
+ // reference to it: the move_only_function's state must be independent of
+ // the original's, and must outlive it.
+ auto Counter = [N = 0]() mutable { return ++N; };
+ move_only_function<int()> F(Counter);
+
+ // Advancing the original must not affect the copy held by F.
+ EXPECT_EQ(Counter(), 1);
+ EXPECT_EQ(F(), 1);
+
+ // F must remain valid after the original goes out of scope.
+ move_only_function<int()> G;
+ {
+ auto Scoped = [N = 41]() mutable { return ++N; };
+ G = move_only_function<int()>(Scoped);
+ }
+ EXPECT_EQ(G(), 42);
+}
+
TEST(MoveOnlyFunctionTest, NoexceptSignature) {
move_only_function<int(int) noexcept> Inc = [](int X) noexcept {
return X + 1;
More information about the llvm-commits
mailing list