diff --git a/libs/pika/async_cuda/include/pika/async_cuda/then_with_stream.hpp b/libs/pika/async_cuda/include/pika/async_cuda/then_with_stream.hpp index 855eb2ef1..a0d7deda8 100644 --- a/libs/pika/async_cuda/include/pika/async_cuda/then_with_stream.hpp +++ b/libs/pika/async_cuda/include/pika/async_cuda/then_with_stream.hpp @@ -249,21 +249,19 @@ namespace pika::cuda::experimental::then_with_stream_detail { } template - auto set_value(Ts&&... ts) && noexcept + auto set_value(Ts&&... ts) noexcept -> decltype(PIKA_INVOKE(PIKA_MOVE(f), op_state.sched, stream.value(), ts...), void()) { - auto r = PIKA_MOVE(*this); pika::detail::try_catch_exception_ptr( [&]() mutable { using ts_element_type = std::tuple...>; - r.op_state.ts.template emplace( - PIKA_FORWARD(Ts, ts)...); - [[maybe_unused]] auto& t = std::get(r.op_state.ts); + op_state.ts.template emplace(PIKA_FORWARD(Ts, ts)...); + [[maybe_unused]] auto& t = std::get(op_state.ts); - if (!r.op_state.stream) + if (!op_state.stream) { - r.op_state.stream.emplace(r.op_state.sched.get_next_stream()); + op_state.stream.emplace(op_state.sched.get_next_stream()); } // If the next receiver is also a @@ -274,11 +272,11 @@ namespace pika::cuda::experimental::then_with_stream_detail { if constexpr (is_then_with_cuda_stream_receiver< std::decay_t>::value) { - if (r.op_state.sched == r.op_state.receiver.op_state.sched) + if (op_state.sched == op_state.receiver.op_state.sched) { - PIKA_ASSERT(r.op_state.stream); - PIKA_ASSERT(!r.op_state.receiver.op_state.stream); - r.op_state.receiver.op_state.stream = r.op_state.stream; + PIKA_ASSERT(op_state.stream); + PIKA_ASSERT(!op_state.receiver.op_state.stream); + op_state.receiver.op_state.stream = op_state.stream; successor_uses_same_stream = true; } @@ -292,8 +290,8 @@ namespace pika::cuda::experimental::then_with_stream_detail { { std::apply( [&](auto&... ts) mutable { - PIKA_INVOKE(PIKA_MOVE(r.op_state.f), r.op_state.sched, - r.op_state.stream.value(), ts...); + PIKA_INVOKE(PIKA_MOVE(op_state.f), op_state.sched, + op_state.stream.value(), ts...); }, t); @@ -309,14 +307,14 @@ namespace pika::cuda::experimental::then_with_stream_detail { // stream when a // non-then_with_cuda_stream receiver is // connected. - set_value_immediate_void(r.op_state); + set_value_immediate_void(op_state); } else { // When the streams are different, we // add a callback which will call // set_value on the receiver. - set_value_event_callback_void(r.op_state); + set_value_event_callback_void(op_state); } } else @@ -325,16 +323,16 @@ namespace pika::cuda::experimental::then_with_stream_detail { // then_with_cuda_stream_receiver, we add a // callback which will call set_value on the // receiver. - set_value_event_callback_void(r.op_state); + set_value_event_callback_void(op_state); } } else { std::apply( [&](auto&... ts) mutable { - r.op_state.result.template emplace( - PIKA_INVOKE(PIKA_MOVE(r.op_state.f), r.op_state.sched, - r.op_state.stream.value(), ts...)); + op_state.result.template emplace( + PIKA_INVOKE(PIKA_MOVE(op_state.f), op_state.sched, + op_state.stream.value(), ts...)); }, t); @@ -350,8 +348,7 @@ namespace pika::cuda::experimental::then_with_stream_detail { // stream when a // non-then_with_cuda_stream receiver is // connected. - set_value_immediate_non_void( - r.op_state); + set_value_immediate_non_void(op_state); } else { @@ -359,7 +356,7 @@ namespace pika::cuda::experimental::then_with_stream_detail { // add a callback which will call // set_value on the receiver. set_value_event_callback_non_void( - r.op_state); + op_state); } } else @@ -368,14 +365,13 @@ namespace pika::cuda::experimental::then_with_stream_detail { // then_with_cuda_stream_receiver, we add a // callback which will call set_value on the // receiver. - set_value_event_callback_non_void( - r.op_state); + set_value_event_callback_non_void(op_state); } } }, [&](std::exception_ptr ep) mutable { pika::execution::experimental::set_error( - PIKA_MOVE(r.op_state.receiver), PIKA_MOVE(ep)); + PIKA_MOVE(op_state.receiver), PIKA_MOVE(ep)); }); } @@ -387,6 +383,25 @@ namespace pika::cuda::experimental::then_with_stream_detail { } }; + // This should be a hidden friend in then_with_cuda_stream_receiver. + // However, nvcc does not know how to compile it with some argument + // types ("error: no instance of overloaded function std::forward + // matches the argument list"). + template + friend auto tag_invoke(pika::execution::experimental::set_value_t, + then_with_cuda_stream_receiver&& r, Ts&&... ts) noexcept + -> decltype(r.set_value(PIKA_FORWARD(Ts, ts)...)) + { + // nvcc fails to compile this with std::forward(ts)... or + // static_cast(ts)... so we explicitly use + // static_cast(ts)... as a workaround. +#if defined(PIKA_HAVE_CUDA) + r.set_value(static_cast(ts)...); +#else + r.set_value(PIKA_FORWARD(Ts, ts)...); +#endif + } + using operation_state_type = pika::execution::experimental::connect_result_t, then_with_cuda_stream_receiver>; diff --git a/libs/pika/async_mpi/include/pika/async_mpi/dispatch_mpi.hpp b/libs/pika/async_mpi/include/pika/async_mpi/dispatch_mpi.hpp index a1bc641e1..71b9366cd 100644 --- a/libs/pika/async_mpi/include/pika/async_mpi/dispatch_mpi.hpp +++ b/libs/pika/async_mpi/include/pika/async_mpi/dispatch_mpi.hpp @@ -117,9 +117,9 @@ namespace pika::mpi::experimental::detail { // otherwise return the request by passing it to set_value template >> - constexpr void set_value(Ts&&... ts) && noexcept + friend constexpr void + tag_invoke(ex::set_value_t, dispatch_mpi_receiver r, Ts&&... ts) noexcept { - auto r = PIKA_MOVE(*this); pika::detail::try_catch_exception_ptr( [&]() mutable { using invoke_result_type = mpi_request_invoke_result_t; diff --git a/libs/pika/async_mpi/include/pika/async_mpi/trigger_mpi.hpp b/libs/pika/async_mpi/include/pika/async_mpi/trigger_mpi.hpp index b466073b7..e5f03f48d 100644 --- a/libs/pika/async_mpi/include/pika/async_mpi/trigger_mpi.hpp +++ b/libs/pika/async_mpi/include/pika/async_mpi/trigger_mpi.hpp @@ -115,10 +115,9 @@ namespace pika::mpi::experimental::detail { // receive the MPI Request and set a callback to be // triggered when the mpi request completes - constexpr void set_value(MPI_Request request) && noexcept + friend constexpr void tag_invoke( + ex::set_value_t, trigger_mpi_receiver r, MPI_Request request) noexcept { - auto r = PIKA_MOVE(*this); - // early exit check if (request == MPI_REQUEST_NULL) { diff --git a/libs/pika/execution/include/pika/execution/algorithms/bulk.hpp b/libs/pika/execution/include/pika/execution/algorithms/bulk.hpp index 550d50d55..324719426 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/bulk.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/bulk.hpp @@ -95,12 +95,11 @@ namespace pika::bulk_detail { } template - void set_value(Ts&&... ts) && noexcept + void set_value(Ts&&... ts) { - auto r = PIKA_MOVE(*this); pika::detail::try_catch_exception_ptr( [&]() { - for (auto const& s : r.shape) { PIKA_INVOKE(r.f, s, ts...); } + for (auto const& s : shape) { PIKA_INVOKE(f, s, ts...); } pika::execution::experimental::set_value( PIKA_MOVE(receiver), PIKA_FORWARD(Ts, ts)...); }, @@ -109,6 +108,20 @@ namespace pika::bulk_detail { PIKA_MOVE(receiver), PIKA_MOVE(ep)); }); } + + template + friend auto tag_invoke( + pika::execution::experimental::set_value_t, bulk_receiver&& r, Ts&&... ts) noexcept + -> decltype(pika::execution::experimental::set_value( + std::declval&&>(), PIKA_FORWARD(Ts, ts)...), + void()) + { + // set_value is in a member function only because of a + // compiler bug in GCC 7. When the body of set_value is + // inlined here compilation fails with an internal compiler + // error. + r.set_value(PIKA_FORWARD(Ts, ts)...); + } }; template diff --git a/libs/pika/execution/include/pika/execution/algorithms/drop_operation_state.hpp b/libs/pika/execution/include/pika/execution/algorithms/drop_operation_state.hpp index a79d154b4..d93f2be88 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/drop_operation_state.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/drop_operation_state.hpp @@ -79,10 +79,9 @@ namespace pika::drop_op_state_detail { }; template - void set_value(Ts&&... ts) && noexcept + friend void tag_invoke(pika::execution::experimental::set_value_t, + drop_op_state_receiver_type r, Ts&&... ts) noexcept { - auto r = PIKA_MOVE(*this); - PIKA_ASSERT(r.op_state != nullptr); PIKA_ASSERT(r.op_state->op_state.has_value()); diff --git a/libs/pika/execution/include/pika/execution/algorithms/drop_value.hpp b/libs/pika/execution/include/pika/execution/algorithms/drop_value.hpp index bc5b2f413..2e165d949 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/drop_value.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/drop_value.hpp @@ -53,9 +53,9 @@ namespace pika::drop_value_detail { } template - void set_value(Ts&&...) && noexcept + friend void tag_invoke(pika::execution::experimental::set_value_t, + drop_value_receiver_type&& r, Ts&&...) noexcept { - auto r = PIKA_MOVE(*this); pika::execution::experimental::set_value(PIKA_MOVE(r.receiver)); } diff --git a/libs/pika/execution/include/pika/execution/algorithms/ensure_started.hpp b/libs/pika/execution/include/pika/execution/algorithms/ensure_started.hpp index b132d73eb..ed4124ff0 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/ensure_started.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/ensure_started.hpp @@ -236,14 +236,14 @@ namespace pika::ensure_started_detail { #endif template - auto set_value(Ts&&... ts) && noexcept + friend auto tag_invoke(pika::execution::experimental::set_value_t, + ensure_started_receiver r, Ts&&... ts) noexcept -> decltype(std::declval< pika::detail::variant>() .template emplace( std::make_tuple<>(PIKA_FORWARD(Ts, ts)...)), void()) { - auto r = PIKA_MOVE(*this); r.state->v.template emplace( std::make_tuple<>(PIKA_FORWARD(Ts, ts)...)); r.state->set_predecessor_done(); diff --git a/libs/pika/execution/include/pika/execution/algorithms/let_error.hpp b/libs/pika/execution/include/pika/execution/algorithms/let_error.hpp index 2f75d2278..e9e8057d1 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/let_error.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/let_error.hpp @@ -196,9 +196,9 @@ namespace pika::let_error_detail { template >> - void set_value(Ts&&... ts) && noexcept + friend void tag_invoke(pika::execution::experimental::set_value_t, + let_error_predecessor_receiver&& r, Ts&&... ts) noexcept { - auto r = PIKA_MOVE(*this); pika::execution::experimental::set_value( PIKA_MOVE(r.receiver), PIKA_FORWARD(Ts, ts)...); } diff --git a/libs/pika/execution/include/pika/execution/algorithms/let_value.hpp b/libs/pika/execution/include/pika/execution/algorithms/let_value.hpp index 4531735f3..74092974e 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/let_value.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/let_value.hpp @@ -231,27 +231,37 @@ namespace pika::let_value_detail { pika::detail::monostate>; template - auto set_value(Ts&&... ts) && noexcept - -> decltype(std::declval() - .template emplace...>>( - PIKA_FORWARD(Ts, ts)...), - void()) + void set_value(Ts&&... ts) { - auto r = PIKA_MOVE(*this); pika::detail::try_catch_exception_ptr( [&]() { - r.op_state.predecessor_ts + op_state.predecessor_ts .template emplace...>>( PIKA_FORWARD(Ts, ts)...); pika::detail::visit( - set_value_visitor{PIKA_MOVE(r.receiver), PIKA_MOVE(f), r.op_state}, - r.op_state.predecessor_ts); + set_value_visitor{PIKA_MOVE(receiver), PIKA_MOVE(f), op_state}, + op_state.predecessor_ts); }, [&](std::exception_ptr ep) { pika::execution::experimental::set_error( - PIKA_MOVE(r.receiver), PIKA_MOVE(ep)); + PIKA_MOVE(receiver), PIKA_MOVE(ep)); }); } + + template + friend auto tag_invoke(pika::execution::experimental::set_value_t, + let_value_predecessor_receiver&& r, Ts&&... ts) noexcept + -> decltype(std::declval() + .template emplace...>>( + PIKA_FORWARD(Ts, ts)...), + void()) + { + // set_value is in a member function only because of a + // compiler bug in GCC 7. When the body of set_value is + // inlined here compilation fails with an internal + // compiler error. + r.set_value(PIKA_FORWARD(Ts, ts)...); + } }; template diff --git a/libs/pika/execution/include/pika/execution/algorithms/require_started.hpp b/libs/pika/execution/include/pika/execution/algorithms/require_started.hpp index 21a5b982b..b9932919d 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/require_started.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/require_started.hpp @@ -120,9 +120,9 @@ namespace pika { }; template - void set_value(Ts&&... ts) && noexcept + friend void tag_invoke(pika::execution::experimental::set_value_t, + require_started_receiver_type r, Ts&&... ts) noexcept { - auto r = PIKA_MOVE(*this); PIKA_ASSERT(r.op_state != nullptr); pika::execution::experimental::set_value( PIKA_MOVE(r.op_state->receiver), PIKA_FORWARD(Ts, ts)...); @@ -381,7 +381,8 @@ namespace pika { s.connected = true; return - { // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + { + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) *std::exchange(s.sender, std::nullopt), PIKA_FORWARD(Receiver, receiver) #if defined(PIKA_DETAIL_HAVE_REQUIRE_STARTED_MODE) , diff --git a/libs/pika/execution/include/pika/execution/algorithms/schedule_from.hpp b/libs/pika/execution/include/pika/execution/algorithms/schedule_from.hpp index 1f8ecaac0..b0485dbcb 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/schedule_from.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/schedule_from.hpp @@ -181,14 +181,21 @@ namespace pika::schedule_from_detail { pika::detail::monostate>; template - auto set_value(Ts&&... ts) && noexcept + friend auto tag_invoke(pika::execution::experimental::set_value_t, + predecessor_sender_receiver&& r, Ts&&... ts) noexcept -> decltype(std::declval() .template emplace...>>( - std::forward(ts)...), + PIKA_FORWARD(Ts, ts)...), void()) { - auto r = std::move(*this); - r.op_state.set_value_predecessor_sender(std::forward(ts)...); + // nvcc fails to compile this with std::forward(ts)... + // or static_cast(ts)... so we explicitly use + // static_cast(ts)... as a workaround. +# if defined(PIKA_HAVE_CUDA) + r.op_state.set_value_predecessor_sender(static_cast(ts)...); +# else + r.op_state.set_value_predecessor_sender(PIKA_FORWARD(Ts, ts)...); +# endif } }; @@ -245,9 +252,9 @@ namespace pika::schedule_from_detail { r.op_state.set_stopped_scheduler_sender(); } - void set_value() && noexcept + friend void tag_invoke(pika::execution::experimental::set_value_t, + scheduler_sender_receiver&& r) noexcept { - auto r = std::move(*this); r.op_state.set_value_scheduler_sender(); } }; diff --git a/libs/pika/execution/include/pika/execution/algorithms/split.hpp b/libs/pika/execution/include/pika/execution/algorithms/split.hpp index 7b4ba6fc3..67e76db59 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/split.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/split.hpp @@ -190,14 +190,14 @@ namespace pika::split_detail { value_type_helper>; template - auto set_value(Ts&&... ts) && noexcept + friend auto tag_invoke(pika::execution::experimental::set_value_t, split_receiver r, + Ts&&... ts) noexcept -> decltype(std::declval< pika::detail::variant>() .template emplace( std::make_tuple<>(PIKA_FORWARD(Ts, ts)...)), void()) { - auto r = PIKA_MOVE(*this); r.state->v.template emplace( std::make_tuple<>(PIKA_FORWARD(Ts, ts)...)); diff --git a/libs/pika/execution/include/pika/execution/algorithms/split_tuple.hpp b/libs/pika/execution/include/pika/execution/algorithms/split_tuple.hpp index 42c43589d..f4c3775ea 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/split_tuple.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/split_tuple.hpp @@ -147,13 +147,13 @@ namespace pika::split_tuple_detail { #endif template - auto set_value(T&& t) && noexcept + friend auto tag_invoke(pika::execution::experimental::set_value_t, + split_tuple_receiver&& r, T&& t) noexcept -> decltype(std::declval< pika::detail::variant>() .template emplace(PIKA_FORWARD(T, t)), void()) { - auto r = PIKA_MOVE(*this); r.state.v.template emplace(PIKA_FORWARD(T, t)); r.state.set_predecessor_done(); diff --git a/libs/pika/execution/include/pika/execution/algorithms/start_detached.hpp b/libs/pika/execution/include/pika/execution/algorithms/start_detached.hpp index d91a61e20..5a82765c0 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/start_detached.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/start_detached.hpp @@ -67,9 +67,9 @@ namespace pika::start_detached_detail { }; template - void set_value(Ts&&...) && noexcept + friend void tag_invoke(pika::execution::experimental::set_value_t, + start_detached_receiver&& r, Ts&&...) noexcept { - auto r = PIKA_MOVE(*this); r.op_state.release(); } }; diff --git a/libs/pika/execution/include/pika/execution/algorithms/sync_wait.hpp b/libs/pika/execution/include/pika/execution/algorithms/sync_wait.hpp index 7a70cbb52..867fb2921 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/sync_wait.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/sync_wait.hpp @@ -177,9 +177,9 @@ namespace pika::sync_wait_detail { template > - void set_value(Us&&... us) && noexcept + friend void tag_invoke(pika::execution::experimental::set_value_t, + sync_wait_receiver_type&& r, Us&&... us) noexcept { - auto r = PIKA_MOVE(*this); r.state.value.template emplace(PIKA_FORWARD(Us, us)...); r.signal_set_called(); } diff --git a/libs/pika/execution/include/pika/execution/algorithms/then.hpp b/libs/pika/execution/include/pika/execution/algorithms/then.hpp index 873eb282f..fb73bf84f 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/then.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/then.hpp @@ -55,10 +55,10 @@ namespace pika::then_detail { pika::execution::experimental::set_stopped(PIKA_MOVE(r.receiver)); } + private: template - void set_value(Ts&&... ts) && noexcept + void set_value_helper(Ts&&... ts) noexcept { - auto r = PIKA_MOVE(*this); pika::detail::try_catch_exception_ptr( [&]() { if constexpr (std::is_void_v>) @@ -66,29 +66,38 @@ namespace pika::then_detail { // Certain versions of GCC with optimizations fail on // the move with an internal compiler error. # if defined(PIKA_GCC_VERSION) && (PIKA_GCC_VERSION < 100000) - PIKA_INVOKE(std::move(r.f), PIKA_FORWARD(Ts, ts)...); + PIKA_INVOKE(std::move(f), PIKA_FORWARD(Ts, ts)...); # else - PIKA_INVOKE(PIKA_MOVE(r.f), PIKA_FORWARD(Ts, ts)...); + PIKA_INVOKE(PIKA_MOVE(f), PIKA_FORWARD(Ts, ts)...); # endif - pika::execution::experimental::set_value(PIKA_MOVE(r.receiver)); + pika::execution::experimental::set_value(PIKA_MOVE(receiver)); } else { // Certain versions of GCC with optimizations fail on // the move with an internal compiler error. # if defined(PIKA_GCC_VERSION) && (PIKA_GCC_VERSION < 100000) - pika::execution::experimental::set_value(PIKA_MOVE(r.receiver), - PIKA_INVOKE(std::move(r.f), PIKA_FORWARD(Ts, ts)...)); + pika::execution::experimental::set_value(PIKA_MOVE(receiver), + PIKA_INVOKE(std::move(f), PIKA_FORWARD(Ts, ts)...)); # else - pika::execution::experimental::set_value(PIKA_MOVE(r.receiver), - PIKA_INVOKE(PIKA_MOVE(r.f), PIKA_FORWARD(Ts, ts)...)); + pika::execution::experimental::set_value(PIKA_MOVE(receiver), + PIKA_INVOKE(PIKA_MOVE(f), PIKA_FORWARD(Ts, ts)...)); # endif } }, [&](std::exception_ptr ep) { - pika::execution::experimental::set_error(PIKA_MOVE(r.receiver), PIKA_MOVE(ep)); + pika::execution::experimental::set_error(PIKA_MOVE(receiver), PIKA_MOVE(ep)); }); } + + template + friend void tag_invoke( + pika::execution::experimental::set_value_t, then_receiver_type&& r, Ts&&... ts) noexcept + { + // GCC 7 fails with an internal compiler error unless the actual + // body is in a helper function. + r.set_value_helper(PIKA_FORWARD(Ts, ts)...); + } }; template diff --git a/libs/pika/execution/include/pika/execution/algorithms/unpack.hpp b/libs/pika/execution/include/pika/execution/algorithms/unpack.hpp index 93eec425b..c528a48a5 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/unpack.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/unpack.hpp @@ -56,9 +56,9 @@ namespace pika::unpack_detail { } template - void set_value(Ts&& ts) && noexcept + friend void tag_invoke( + pika::execution::experimental::set_value_t, unpack_receiver_type&& r, Ts&& ts) noexcept { - auto r = PIKA_MOVE(*this); std::apply(pika::util::detail::bind_front( pika::execution::experimental::set_value, PIKA_MOVE(r.receiver)), PIKA_FORWARD(Ts, ts)); diff --git a/libs/pika/execution/include/pika/execution/algorithms/when_all.hpp b/libs/pika/execution/include/pika/execution/algorithms/when_all.hpp index ca0685d23..fb5f41369 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/when_all.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/when_all.hpp @@ -102,13 +102,12 @@ namespace pika::when_all_impl { typename pika::util::detail::make_index_pack::type; template - auto set_value(Ts&&... ts) && noexcept + auto set_value(Ts&&... ts) noexcept -> decltype(set_value_helper(index_pack_type{}, PIKA_FORWARD(Ts, ts)...), void()) { - auto r = std::move(*this); if constexpr (OperationState::sender_pack_size > 0) { - if (!r.op_state.set_stopped_error_called) + if (!op_state.set_stopped_error_called) { try { @@ -116,19 +115,35 @@ namespace pika::when_all_impl { } catch (...) { - if (!r.op_state.set_stopped_error_called.exchange(true)) + if (!op_state.set_stopped_error_called.exchange(true)) { // NOLINTNEXTLINE(bugprone-throw-keyword-missing) - r.op_state.error = std::current_exception(); + op_state.error = std::current_exception(); } } } } - r.op_state.finish(); + op_state.finish(); } }; + // Due to what appears to be a bug in clang this is not a hidden friend + // of when_all_receiver. The trailing decltype for SFINAE in the member + // set_value would give an error about accessing an incomplete type, if + // the member set_value were a hidden friend tag_invoke overload + // instead. Note that the receiver is unconstrained. That is because + // OperationState in when_all_receiver cannot be deduced + // when when_all_receiver is an alias template. Since this is in a + // unique namespace nothing but when_all_receiver should ever find this + // overload. + template + auto tag_invoke(pika::execution::experimental::set_value_t, Receiver&& r, Ts&&... ts) noexcept + -> decltype(r.set_value(PIKA_FORWARD(Ts, ts)...), void()) + { + r.set_value(PIKA_FORWARD(Ts, ts)...); + } + template struct when_all_sender_impl { diff --git a/libs/pika/execution/include/pika/execution/algorithms/when_all_vector.hpp b/libs/pika/execution/include/pika/execution/algorithms/when_all_vector.hpp index 53954baff..95cfab435 100644 --- a/libs/pika/execution/include/pika/execution/algorithms/when_all_vector.hpp +++ b/libs/pika/execution/include/pika/execution/algorithms/when_all_vector.hpp @@ -176,9 +176,9 @@ namespace pika::when_all_vector_detail { }; template - void set_value(Ts&&... ts) && noexcept + friend void tag_invoke(pika::execution::experimental::set_value_t, + when_all_vector_receiver&& r, Ts&&... ts) noexcept { - auto r = PIKA_MOVE(*this); if (!r.op_state.set_stopped_error_called) { try diff --git a/libs/pika/execution_base/include/pika/execution_base/any_sender.hpp b/libs/pika/execution_base/include/pika/execution_base/any_sender.hpp index 7d2f1b0ab..749a65f81 100644 --- a/libs/pika/execution_base/include/pika/execution_base/any_sender.hpp +++ b/libs/pika/execution_base/include/pika/execution_base/any_sender.hpp @@ -539,10 +539,10 @@ namespace pika::execution::experimental::detail { any_receiver& operator=(any_receiver const&) = delete; template - auto set_value(Ts_&&... ts) && noexcept + friend auto tag_invoke( + pika::execution::experimental::set_value_t, any_receiver&& r, Ts_&&... ts) noexcept -> decltype(std::declval().set_value(PIKA_FORWARD(Ts_, ts)...)) { - auto r = PIKA_MOVE(*this); // We first move the storage to a temporary variable so that // this any_receiver is empty after this set_value. Doing // PIKA_MOVE(storage.get()).set_value(...) would leave us with a diff --git a/libs/pika/execution_base/include/pika/execution_base/receiver.hpp b/libs/pika/execution_base/include/pika/execution_base/receiver.hpp index 5006d7aa3..94b0b1a39 100644 --- a/libs/pika/execution_base/include/pika/execution_base/receiver.hpp +++ b/libs/pika/execution_base/include/pika/execution_base/receiver.hpp @@ -131,18 +131,8 @@ namespace pika::execution::experimental { struct is_receiver_of; PIKA_HOST_DEVICE_INLINE_CONSTEXPR_VARIABLE - struct set_value_t + struct set_value_t : pika::functional::detail::tag { - template - PIKA_FORCEINLINE constexpr auto - PIKA_STATIC_CALL_OPERATOR(Receiver&& receiver, Ts&&... ts) noexcept - -> decltype(std::forward(receiver).set_value(std::forward(ts)...)) - { - static_assert( - noexcept(std::forward(receiver).set_value(std::forward(ts)...)), - "std::execution receiver set_value member function must be noexcept"); - return std::forward(receiver).set_value(std::forward(ts)...); - } } set_value{}; PIKA_HOST_DEVICE_INLINE_CONSTEXPR_VARIABLE diff --git a/libs/pika/execution_base/tests/include/pika/execution_base/tests/algorithm_test_utils.hpp b/libs/pika/execution_base/tests/include/pika/execution_base/tests/algorithm_test_utils.hpp index b0afb2d16..30ba0aa11 100644 --- a/libs/pika/execution_base/tests/include/pika/execution_base/tests/algorithm_test_utils.hpp +++ b/libs/pika/execution_base/tests/include/pika/execution_base/tests/algorithm_test_utils.hpp @@ -151,10 +151,10 @@ struct callback_receiver }; template - auto set_value(Ts&&... ts) && noexcept + friend auto tag_invoke( + pika::execution::experimental::set_value_t, callback_receiver&& r, Ts&&... ts) noexcept -> decltype(PIKA_INVOKE(std::declval>(), std::forward(ts)...), void()) { - auto r = PIKA_MOVE(*this); PIKA_INVOKE(r.f, std::forward(ts)...); r.set_value_called = true; } @@ -190,9 +190,9 @@ struct error_callback_receiver }; template - void set_value(Ts&&...) && noexcept + friend void tag_invoke( + pika::execution::experimental::set_value_t, error_callback_receiver&& r, Ts&&...) noexcept { - auto r = PIKA_MOVE(*this); PIKA_TEST(r.expect_set_value); } diff --git a/libs/pika/execution_base/tests/unit/any_sender.cpp b/libs/pika/execution_base/tests/unit/any_sender.cpp index aed94e7ee..de28efbfa 100644 --- a/libs/pika/execution_base/tests/unit/any_sender.cpp +++ b/libs/pika/execution_base/tests/unit/any_sender.cpp @@ -241,7 +241,8 @@ struct error_receiver }; template - void set_value(Ts&&...) && noexcept + friend void + tag_invoke(pika::execution::experimental::set_value_t, error_receiver&&, Ts&&...) noexcept { PIKA_TEST(false); } diff --git a/libs/pika/execution_base/tests/unit/basic_receiver.cpp b/libs/pika/execution_base/tests/unit/basic_receiver.cpp index 66a8406de..3b2d57634 100644 --- a/libs/pika/execution_base/tests/unit/basic_receiver.cpp +++ b/libs/pika/execution_base/tests/unit/basic_receiver.cpp @@ -31,7 +31,7 @@ namespace mylib { error_called = true; } - void set_value(int) && noexcept { value_called = true; } + friend void tag_invoke(ex::set_value_t, receiver_1&&, int) noexcept { value_called = true; } friend constexpr ex::empty_env tag_invoke(ex::get_env_t, receiver_1 const&) noexcept { @@ -64,7 +64,7 @@ namespace mylib { error_called = true; } - void set_value(int) && noexcept { value_called = true; } + friend void tag_invoke(ex::set_value_t, receiver_3, int) noexcept { value_called = true; } friend constexpr ex::empty_env tag_invoke(ex::get_env_t, receiver_3 const&) noexcept { @@ -81,7 +81,10 @@ namespace mylib { error_called = true; } - void set_value(int) noexcept { value_called = true; } + friend void tag_invoke(ex::set_value_t, non_receiver_1, int) noexcept + { + value_called = true; + } }; struct non_receiver_2 @@ -93,7 +96,10 @@ namespace mylib { error_called = true; } - void set_value(int) && noexcept { value_called = true; } + friend void tag_invoke(ex::set_value_t, non_receiver_2, int) noexcept + { + value_called = true; + } }; struct non_receiver_3 @@ -105,7 +111,10 @@ namespace mylib { error_called = true; } - void set_value(int) noexcept { value_called = true; } + friend void tag_invoke(ex::set_value_t, non_receiver_3, int) noexcept + { + value_called = true; + } }; struct non_receiver_4 @@ -119,7 +128,10 @@ namespace mylib { error_called = true; } - void set_value(int) & noexcept { value_called = true; } + friend void tag_invoke(ex::set_value_t, non_receiver_4&, int) noexcept + { + value_called = true; + } friend constexpr ex::empty_env tag_invoke(ex::get_env_t, non_receiver_4 const&) noexcept { diff --git a/libs/pika/execution_base/tests/unit/basic_sender.cpp b/libs/pika/execution_base/tests/unit/basic_sender.cpp index d1984f7d4..c31b48710 100644 --- a/libs/pika/execution_base/tests/unit/basic_sender.cpp +++ b/libs/pika/execution_base/tests/unit/basic_sender.cpp @@ -74,7 +74,7 @@ struct receiver friend void tag_invoke(ex::set_stopped_t, receiver&&) noexcept {} - void set_value(int v) && noexcept { i.get() = v; } + friend void tag_invoke(ex::set_value_t, receiver&& r, int v) noexcept { r.i.get() = v; } friend constexpr ex::empty_env tag_invoke(ex::get_env_t, receiver const&) noexcept { @@ -156,7 +156,10 @@ struct void_receiver friend void tag_invoke(ex::set_stopped_t, void_receiver&&) noexcept {} - void set_value() && noexcept { ++void_receiver_set_value_calls; } + friend void tag_invoke(ex::set_value_t, void_receiver&&) noexcept + { + ++void_receiver_set_value_calls; + } friend constexpr ex::empty_env tag_invoke(ex::get_env_t, void_receiver const&) noexcept { diff --git a/libs/pika/executors/include/pika/executors/thread_pool_scheduler_bulk.hpp b/libs/pika/executors/include/pika/executors/thread_pool_scheduler_bulk.hpp index 73adb150d..e0a3a25b5 100644 --- a/libs/pika/executors/include/pika/executors/thread_pool_scheduler_bulk.hpp +++ b/libs/pika/executors/include/pika/executors/thread_pool_scheduler_bulk.hpp @@ -389,9 +389,9 @@ namespace pika::thread_pool_bulk_detail { } template - void set_value(Ts&&... ts) && noexcept + friend void tag_invoke(pika::execution::experimental::set_value_t, + bulk_receiver&& r, Ts&&... ts) noexcept { - auto r = PIKA_MOVE(*this); // Don't spawn tasks if there is no work to be done if (r.op_state->shape == 0) { diff --git a/libs/pika/executors/tests/unit/std_thread_scheduler.cpp b/libs/pika/executors/tests/unit/std_thread_scheduler.cpp index 0fc7dba3e..2a2056073 100644 --- a/libs/pika/executors/tests/unit/std_thread_scheduler.cpp +++ b/libs/pika/executors/tests/unit/std_thread_scheduler.cpp @@ -68,13 +68,13 @@ struct check_context_receiver } template - void set_value(Ts&&...) && noexcept + friend void tag_invoke(ex::set_value_t, check_context_receiver&& r, Ts&&...) noexcept { - PIKA_TEST_NEQ(parent_id, std::this_thread::get_id()); + PIKA_TEST_NEQ(r.parent_id, std::this_thread::get_id()); PIKA_TEST_NEQ(std::thread::id(), std::this_thread::get_id()); - std::lock_guard l{mtx}; - executed = true; - cond.notify_one(); + std::lock_guard l{r.mtx}; + r.executed = true; + r.cond.notify_one(); } friend constexpr pika::execution::experimental::empty_env tag_invoke( @@ -226,9 +226,8 @@ struct callback_receiver friend void tag_invoke(ex::set_stopped_t, callback_receiver&&) noexcept { PIKA_TEST(false); } template - void set_value(Ts&&...) && noexcept + friend void tag_invoke(ex::set_value_t, callback_receiver&& r, Ts&&...) noexcept { - auto r = PIKA_MOVE(*this); r.f(); std::lock_guard l{r.mtx}; r.executed = true; diff --git a/libs/pika/executors/tests/unit/thread_pool_scheduler.cpp b/libs/pika/executors/tests/unit/thread_pool_scheduler.cpp index 7d601e46d..3436b3a5d 100644 --- a/libs/pika/executors/tests/unit/thread_pool_scheduler.cpp +++ b/libs/pika/executors/tests/unit/thread_pool_scheduler.cpp @@ -75,14 +75,14 @@ struct check_context_receiver } template - void set_value(Ts&&...) && noexcept + friend void tag_invoke(ex::set_value_t, check_context_receiver&& r, Ts&&...) noexcept { - PIKA_TEST_NEQ(parent_id, pika::this_thread::get_id()); + PIKA_TEST_NEQ(r.parent_id, pika::this_thread::get_id()); PIKA_TEST_NEQ(pika::thread::id(pika::threads::detail::invalid_thread_id), pika::this_thread::get_id()); - std::lock_guard l{mtx}; - executed = true; - cond.notify_one(); + std::lock_guard l{r.mtx}; + r.executed = true; + r.cond.notify_one(); } friend constexpr pika::execution::experimental::empty_env tag_invoke( @@ -234,12 +234,12 @@ struct callback_receiver friend void tag_invoke(ex::set_stopped_t, callback_receiver&&) noexcept { PIKA_TEST(false); } template - void set_value(Ts&&...) && noexcept + friend void tag_invoke(ex::set_value_t, callback_receiver&& r, Ts&&...) noexcept { - f(); - std::lock_guard l{mtx}; - executed = true; - cond.notify_one(); + r.f(); + std::lock_guard l{r.mtx}; + r.executed = true; + r.cond.notify_one(); } friend constexpr pika::execution::experimental::empty_env tag_invoke(