[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 06:31:24 PDT 2026


https://github.com/Kim-J-Smith updated https://github.com/llvm/llvm-project/pull/228868

>From e651fa36d0a56aa56689cd900737353e8222791f Mon Sep 17 00:00:00 2001
From: Kim-J-Smith <3440910457 at qq.com>
Date: Sun, 4 Oct 2026 17:20:21 +0800
Subject: [PATCH 1/2] [libc++] Fix ill-formed return in function_ref's
 static-call path

---
 .../include/__functional/function_ref_impl.h  |  5 ++-
 .../func.wrap.ref.inv/invoke.pass.cpp         | 37 +++++++++++++++++++
 2 files changed, 41 insertions(+), 1 deletion(-)

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() {

>From e623be792418a6798d914cdc255d27086549a144 Mon Sep 17 00:00:00 2001
From: Kim-J-Smith <3440910457 at qq.com>
Date: Sun, 4 Oct 2026 21:30:54 +0800
Subject: [PATCH 2/2] test: passing an argument to the operator() and mutate
 the argument.

---
 .../func.wrap.ref.inv/invoke.pass.cpp         | 46 ++++++++++---------
 1 file changed, 24 insertions(+), 22 deletions(-)

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 f2aff57497da5..2b87c92eaafac 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
@@ -94,8 +94,10 @@ struct TrackCopyMove {
 };
 
 struct StaticallyCallable {
-  static inline int static_call_count = 0;
-  static int operator()() noexcept { static_call_count++; return 42; }
+  static int operator()(int& out) noexcept {
+    out = 42;
+    return 42;
+  }
 };
 
 void test_default() {
@@ -201,12 +203,12 @@ void test_default() {
     f(TrackCopyMove{});
   }
   {
-    // Non-void static operator() can bind to function_ref<void()>
+    // Non-void static operator() can bind to function_ref<void(int&)>
+    int expect_42 = 0;
     StaticallyCallable sc;
-    std::function_ref<void()> f = sc;
-    StaticallyCallable::static_call_count = 0;
-    f();
-    assert(StaticallyCallable::static_call_count == 1);
+    std::function_ref<void(int&)> f = sc;
+    f(expect_42);
+    assert(expect_42 == 42);
   }
 }
 
@@ -313,12 +315,12 @@ void test_const() {
     f(TrackCopyMove{});
   }
   {
-    // Non-void static operator() can bind to function_ref<void()>
+    // Non-void static operator() can bind to function_ref<void(int&) const>
+    int expect_42 = 0;
     StaticallyCallable sc;
-    std::function_ref<void() const> f = sc;
-    StaticallyCallable::static_call_count = 0;
-    f();
-    assert(StaticallyCallable::static_call_count == 1);
+    std::function_ref<void(int&) const> f = sc;
+    f(expect_42);
+    assert(expect_42 == 42);
   }
 }
 
@@ -426,12 +428,12 @@ void test_noexcept() {
     f(TrackCopyMove{});
   }
   {
-    // Non-void static operator() can bind to function_ref<void()>
+    // Non-void static operator() can bind to function_ref<void(int&) noexcept>
+    int expect_42 = 0;
     StaticallyCallable sc;
-    std::function_ref<void() noexcept> f = sc;
-    StaticallyCallable::static_call_count = 0;
-    f();
-    assert(StaticallyCallable::static_call_count == 1);
+    std::function_ref<void(int&) noexcept> f = sc;
+    f(expect_42);
+    assert(expect_42 == 42);
   }
 }
 
@@ -539,12 +541,12 @@ void test_const_noexcept() {
     f(TrackCopyMove{});
   }
   {
-    // Non-void static operator() can bind to function_ref<void()>
+    // Non-void static operator() can bind to function_ref<void(int&) const noexcept>
+    int expect_42 = 0;
     StaticallyCallable sc;
-    std::function_ref<void() const noexcept> f = sc;
-    StaticallyCallable::static_call_count = 0;
-    f();
-    assert(StaticallyCallable::static_call_count == 1);
+    std::function_ref<void(int&) const noexcept> f = sc;
+    f(expect_42);
+    assert(expect_42 == 42);
   }
 }
 



More information about the libcxx-commits mailing list