[libcxx-commits] [libcxx] [libc++] Fix ill-formed return in function_ref's static-call path (PR #228868)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Oct 4 02:53:10 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Kim-J-Smith
<details>
<summary>Changes</summary>
In `libcxx/include/__functional/function_ref_impl.h`:
The current `__statically_callable` branch is ill-formed when the lambda's return type `_Rp` is void but the target's static operator() returns non-void. This patch discards the return value when `_Rp` is void to make it behave the same as `invoke_r<_Rp>`.
Fixes errors like this:
```cpp
#include <functional>
struct F { static int operator()() { return 42; } };
int main() {
std::invoke_r<void>(F{}); // OK
std::function_ref<void()> f = F{}; // error: void lambda should not return a value
}
```
Also adds test coverage binding a non-void static `operator()` to `function_ref<void(...)>` in all four cv/noexcept variants.
Fixes #<!-- -->228717
---
Full diff: https://github.com/llvm/llvm-project/pull/228868.diff
2 Files Affected:
- (modified) libcxx/include/__functional/function_ref_impl.h (+4-1)
- (modified) libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp (+37)
``````````diff
diff --git a/libcxx/include/__functional/function_ref_impl.h b/libcxx/include/__functional/function_ref_impl.h
index ada14643ec496..b54d359853e9b 100644
--- a/libcxx/include/__functional/function_ref_impl.h
+++ b/libcxx/include/__functional/function_ref_impl.h
@@ -106,7 +106,10 @@ class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexc
using _Dn = remove_cv_t<_Tp>;
if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
__call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
- return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
+ if constexpr (is_void_v<_Rp>)
+ _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
+ else
+ return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
};
} else {
__storage_ = __storage_t(std::addressof(__obj)),
diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
index 820fed5fc76bc..f2aff57497da5 100644
--- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.ref/func.wrap.ref.inv/invoke.pass.cpp
@@ -93,6 +93,11 @@ struct TrackCopyMove {
}
};
+struct StaticallyCallable {
+ static inline int static_call_count = 0;
+ static int operator()() noexcept { static_call_count++; return 42; }
+};
+
void test_default() {
{
std::function_ref<void()> f = [] {};
@@ -195,6 +200,14 @@ void test_default() {
std::function_ref<void(TrackCopyMove)> f = lambda;
f(TrackCopyMove{});
}
+ {
+ // Non-void static operator() can bind to function_ref<void()>
+ StaticallyCallable sc;
+ std::function_ref<void()> f = sc;
+ StaticallyCallable::static_call_count = 0;
+ f();
+ assert(StaticallyCallable::static_call_count == 1);
+ }
}
void test_const() {
@@ -299,6 +312,14 @@ void test_const() {
std::function_ref<void(TrackCopyMove) const> f = lambda;
f(TrackCopyMove{});
}
+ {
+ // Non-void static operator() can bind to function_ref<void()>
+ StaticallyCallable sc;
+ std::function_ref<void() const> f = sc;
+ StaticallyCallable::static_call_count = 0;
+ f();
+ assert(StaticallyCallable::static_call_count == 1);
+ }
}
void test_noexcept() {
@@ -404,6 +425,14 @@ void test_noexcept() {
std::function_ref<void(TrackCopyMove) noexcept> f = lambda;
f(TrackCopyMove{});
}
+ {
+ // Non-void static operator() can bind to function_ref<void()>
+ StaticallyCallable sc;
+ std::function_ref<void() noexcept> f = sc;
+ StaticallyCallable::static_call_count = 0;
+ f();
+ assert(StaticallyCallable::static_call_count == 1);
+ }
}
void test_const_noexcept() {
@@ -509,6 +538,14 @@ void test_const_noexcept() {
std::function_ref<void(TrackCopyMove) const noexcept> f = lambda;
f(TrackCopyMove{});
}
+ {
+ // Non-void static operator() can bind to function_ref<void()>
+ StaticallyCallable sc;
+ std::function_ref<void() const noexcept> f = sc;
+ StaticallyCallable::static_call_count = 0;
+ f();
+ assert(StaticallyCallable::static_call_count == 1);
+ }
}
void test() {
``````````
</details>
https://github.com/llvm/llvm-project/pull/228868
More information about the libcxx-commits
mailing list