]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Add static_assert to std::packaged_task::packaged_task(F&&)
authorJonathan Wakely <jwakely@redhat.com>
Mon, 10 Mar 2025 14:29:36 +0000 (14:29 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Tue, 1 Apr 2025 08:58:23 +0000 (09:58 +0100)
LWG 4154 (approved in Wrocław, November 2024) fixed the Mandates:
precondition for std::packaged_task::packaged_task(F&&) to match what
the implementation actually requires. We already gave a diagnostic in
the right cases as required by the issue resolution, so strictly
speaking we don't need to do anything. But the current diagnostic comes
from inside the implementation of std::__invoke_r and could be more
user-friendly.

For C++17 (when std::is_invocable_r_v is available) add a static_assert
to the constructor, so the error is clear:

.../include/c++/15.0.1/future: In instantiation of 'std::packaged_task<_Res(_ArgTypes ...)>::packaged_task(_Fn&&) [with _Fn = const F&; <template-parameter-2-2> = void; _Res = void; _ArgTypes = {}]':
lwg4154_neg.cc:15:31:   required from here
   15 | std::packaged_task<void()> p(f); // { dg-error "here" "" { target c++17 } }
      |                               ^
.../include/c++/15.0.1/future:1575:25: error: static assertion failed
 1575 |           static_assert(is_invocable_r_v<_Res, decay_t<_Fn>&, _ArgTypes...>);
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Also add a test to confirm we get a diagnostic as the standard requires.

libstdc++-v3/ChangeLog:

* include/std/future (packaged_task::packaged_task(F&&)): Add
static_assert.
* testsuite/30_threads/packaged_task/cons/dangling_ref.cc: Add
dg-error for new static assertion.
* testsuite/30_threads/packaged_task/cons/lwg4154_neg.cc: New
test.

Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com>
(cherry picked from commit 4d2683b04fd329c97e3da09498345fe3ee00455f)

libstdc++-v3/include/std/future
libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc
libstdc++-v3/testsuite/30_threads/packaged_task/cons/lwg4154_neg.cc [new file with mode: 0644]

index 9e75ae98b13d2b0cc10f48f2531b3beae6958c76..381db1c00c0fb99a4c690de41ec6c655626c6d87 100644 (file)
@@ -1565,7 +1565,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        packaged_task(_Fn&& __fn)
        : _M_state(
            __create_task_state<_Res(_ArgTypes...)>(std::forward<_Fn>(__fn)))
-       { }
+       {
+#ifdef __cpp_lib_is_invocable // C++ >= 17
+         // _GLIBCXX_RESOLVE_LIB_DEFECTS
+         // 4154. The Mandates for std::packaged_task's constructor
+         // from a callable entity should consider decaying
+         static_assert(is_invocable_r_v<_Res, decay_t<_Fn>&, _ArgTypes...>);
+#endif
+       }
 
 #if __cplusplus < 201703L
       // _GLIBCXX_RESOLVE_LIB_DEFECTS
index e9edb5edc8bed4234b69ea8a2609ec916ac69d95..9b3434a0bf6fa1f80ab8c58227e7e01e9c878cb4 100644 (file)
@@ -9,3 +9,4 @@ std::packaged_task<const int&()> task(f);
 // { dg-error "dangling reference" "" { target { c++14_down } } 0 }
 // { dg-error "no matching function" "" { target c++17 } 0 }
 // { dg-error "enable_if" "" { target c++17 } 0 }
+// { dg-error "static assertion failed" "" { target c++17 } 0 }
diff --git a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/lwg4154_neg.cc b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/lwg4154_neg.cc
new file mode 100644 (file)
index 0000000..6ba1bb1
--- /dev/null
@@ -0,0 +1,38 @@
+// { dg-do compile { target c++11 } }
+
+// LWG 4154. The Mandates for std::packaged_task's constructor from
+// a callable entity should consider decaying
+
+#include <future>
+
+struct F {
+  void operator()() & = delete;
+  void operator()() const & { }
+};
+
+// Mandates: is_invocable_r_v<R, decay_t<F>&, ArgTypes...> is true.
+const F f;
+std::packaged_task<void()> p(f); // { dg-error "here" "" { target c++17 } }
+// { dg-error "static assertion failed" "" { target c++17 } 0 }
+// { dg-error "invoke_r" "" { target *-*-* } 0 }
+// { dg-prune-output "enable_if<false" }
+
+// Only callable as rvalue
+struct Frv {
+  int* operator()() && { return 0; }
+};
+std::packaged_task<int*()> p2(Frv{}); // { dg-error "here" "" { target c++17 } }
+
+// Only callable as non-const lvalue
+struct Fnc {
+  void operator()() const & = delete;
+  void operator()() & { }
+};
+
+// In C++11/14/17/20 std::packaged_task::packaged_task<F>(F&&) incorrectly
+// required that the callable passed to the constructor can be invoked.
+// If the type of the parameter is const F& we might not be able to invoke
+// the parameter, but that's OK because we store and invoke a non-const F.
+// So this should work without errors:
+const Fnc fnc;
+std::packaged_task<void()> p3(fnc);