On Wed, Mar 12, 2025 at 11:53 AM Jonathan Wakely <jwak...@redhat.com> wrote:

> >Could you add a test for functor that is only rvalue-callable (only
> >operator() &&).
> >There was discussion about this case on reflector, so I think it would be
> >good to document that they are not supported.
>
> Like this?
>
Yes. LGTM now.

>
>
>
> 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.
>
> ---
>  libstdc++-v3/include/std/future               |  9 ++++-
>  .../packaged_task/cons/dangling_ref.cc        |  1 +
>  .../packaged_task/cons/lwg4154_neg.cc         | 38 +++++++++++++++++++
>  3 files changed, 47 insertions(+), 1 deletion(-)
>  create mode 100644
> libstdc++-v3/testsuite/30_threads/packaged_task/cons/lwg4154_neg.cc
>
> diff --git a/libstdc++-v3/include/std/future
> b/libstdc++-v3/include/std/future
> index 2a855f262a0..b7ab233b85f 100644
> --- a/libstdc++-v3/include/std/future
> +++ b/libstdc++-v3/include/std/future
> @@ -1567,7 +1567,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
> diff --git
> a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc
> b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc
> index 225b65fe6a7..51c6ade91c3 100644
> --- a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc
> +++ b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc
> @@ -10,3 +10,4 @@ std::packaged_task<const int&()> task(f);
>  // { dg-error "reference to temporary" "" { 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
> index 00000000000..6ba1bb1a53e
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/lwg4154_neg.cc
> @@ -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);
> --
> 2.48.1
>
>

Reply via email to