stdexec: update to latest commit
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: Ie95101c4fce1ec9297053d3c0e3ac265ad4f3960
diff --git a/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp b/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp
new file mode 100644
index 0000000..e582f3d
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__basic_sender.hpp
@@ -0,0 +1,453 @@
+/*
+ * Copyright (c) 2021-2022 NVIDIA Corporation
+ *
+ * Licensed under the Apache License Version 2.0 with LLVM Exceptions
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://llvm.org/LICENSE.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "../concepts.hpp"
+#include "__execution_fwd.hpp"
+#include "__meta.hpp"
+#include "__type_traits.hpp"
+
+#include <utility> // for tuple_size/tuple_element
+
+namespace stdexec
+{
+/////////////////////////////////////////////////////////////////////////////
+// Generic __sender type
+namespace __detail
+{
+template <class _Sender>
+using __impl_of = decltype((__declval<_Sender>().__impl_));
+
+struct __get_tag
+{
+ template <class _Tag, class... _Rest>
+ _Tag operator()(_Tag, _Rest&&...) const noexcept
+ {
+ return {};
+ }
+};
+
+struct __get_data
+{
+ template <class _Data, class... _Rest>
+ _Data&& operator()(__ignore, _Data&& __data, _Rest&&...) const noexcept
+ {
+ return (_Data&&)__data;
+ }
+};
+
+template <class _Continuation>
+struct __get_children
+{
+ template <class... _Children>
+ auto operator()(__ignore, __ignore, _Children&&...) const noexcept
+ -> __mtype<__minvoke<_Continuation, _Children...>> (*)()
+ {
+ return nullptr;
+ }
+};
+
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_GNU("-Wunused-local-typedefs")
+
+struct __get_meta
+{
+ template <class _Tag, class _Data, class... _Children>
+ constexpr auto operator()(_Tag, _Data&&, _Children&&...) const noexcept
+ {
+ struct __meta
+ {
+ using __tag = _Tag;
+ using __data = _Data;
+ using __children = __types<_Children...>;
+ };
+
+ return __meta{};
+ }
+};
+
+STDEXEC_PRAGMA_POP()
+
+struct __tie
+{
+ template <class _Tag, class _Data, class... _Children>
+ constexpr auto operator()(_Tag, _Data&& __data,
+ _Children&&... __children) const noexcept
+ {
+ return std::tuple<_Tag, _Data&&, _Children&&...>{
+ {}, (_Data&&)__data, (_Children&&)__children...};
+ }
+};
+} // namespace __detail
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// __sexpr
+template <class...>
+struct __sexpr
+{
+ using __id = __sexpr;
+ using __t = __sexpr;
+};
+
+template <class _ImplFn>
+struct __sexpr<_ImplFn>
+{
+ using is_sender = void;
+ using __t = __sexpr;
+ using __id = __sexpr;
+ using __meta_t = __call_result_t<_ImplFn, __cp, __detail::__get_meta>;
+ using __tag_t = typename __meta_t::__tag;
+ using __data_t = typename __meta_t::__data;
+ using __children_t = typename __meta_t::__children;
+ using __arity_t = __mapply<__msize, __children_t>;
+
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ static __tag_t __tag() noexcept
+ {
+ return {};
+ }
+
+ mutable _ImplFn __impl_;
+
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ explicit __sexpr(_ImplFn __impl) : __impl_((_ImplFn&&)__impl) {}
+
+ template <same_as<get_env_t> _Tag, same_as<__sexpr> _Self>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ friend auto tag_invoke(_Tag, const _Self& __self) noexcept //
+ -> __msecond<__if_c<same_as<_Tag, get_env_t>>, //
+ decltype(__self.__tag().get_env(__self))>
+ {
+ static_assert(noexcept(__self.__tag().get_env(__self)));
+ return __tag_t::get_env(__self);
+ }
+
+ template <same_as<get_completion_signatures_t> _Tag,
+ __decays_to<__sexpr> _Self, class _Env>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ friend auto tag_invoke(_Tag, _Self&& __self, _Env&& __env) //
+ -> __msecond<__if_c<same_as<_Tag, get_completion_signatures_t>>,
+ decltype(__self.__tag().get_completion_signatures(
+ (_Self&&)__self, (_Env&&)__env))>
+ {
+ return {};
+ }
+
+ // BUGBUG fix receiver constraint here:
+ template <same_as<connect_t> _Tag, __decays_to<__sexpr> _Self,
+ /*receiver*/ class _Receiver>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ friend auto tag_invoke(_Tag, _Self&& __self, _Receiver&& __rcvr) //
+ noexcept(noexcept(__self.__tag().connect((_Self&&)__self,
+ (_Receiver&&)__rcvr))) //
+ -> __msecond<__if_c<same_as<_Tag, connect_t>>,
+ decltype(__self.__tag().connect((_Self&&)__self,
+ (_Receiver&&)__rcvr))>
+ {
+ return __tag_t::connect((_Self&&)__self, (_Receiver&&)__rcvr);
+ }
+
+ template <class _Sender, class _ApplyFn>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ STDEXEC_DEFINE_EXPLICIT_THIS_MEMFN(auto apply)(this _Sender&& __sndr,
+ _ApplyFn&& __fun) //
+ noexcept(__nothrow_callable<__detail::__impl_of<_Sender>,
+ __copy_cvref_fn<_Sender>, _ApplyFn>) //
+ -> __call_result_t<__detail::__impl_of<_Sender>,
+ __copy_cvref_fn<_Sender>, _ApplyFn>
+ { //
+ return ((_Sender&&)__sndr)
+ .__impl_(__copy_cvref_fn<_Sender>(), (_ApplyFn&&)__fun); //
+ }
+
+ template <std::size_t _Idx, __decays_to_derived_from<__sexpr> _Self>
+ STDEXEC_ATTRIBUTE((always_inline))
+ friend decltype(auto) get(_Self&& __self) noexcept
+ requires(_Idx < (__v<__arity_t> + 2))
+ {
+ if constexpr (_Idx == 0)
+ {
+ return __tag_t();
+ }
+ else
+ {
+ return __self.__impl_(__copy_cvref_fn<_Self>(),
+ __nth_pack_element<_Idx>);
+ }
+ STDEXEC_UNREACHABLE();
+ }
+};
+
+template <class _ImplFn>
+STDEXEC_ATTRIBUTE((host, device))
+__sexpr(_ImplFn) -> __sexpr<_ImplFn>;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// __make_sexpr
+namespace __detail
+{
+template <class _Tag>
+struct __make_sexpr_t
+{
+ template <class _Data = __, class... _Children>
+ constexpr auto operator()(_Data __data = {}, _Children... __children) const;
+};
+
+#if STDEXEC_NVHPC() || (STDEXEC_GCC() && __GNUC__ < 13)
+// The NVIDIA HPC compiler and gcc prior to v13 struggle with capture
+// initializers for a parameter pack. As a workaround, we use a wrapper that
+// performs moves when non-const lvalues are copied. That constructor is
+// only used when capturing the variables, never when the resulting lambda
+// is copied or moved.
+
+// Move-by-copy
+template <class _Ty>
+struct __mbc
+{
+ template <class _Cvref>
+ using __f = __minvoke<_Cvref, _Ty>;
+
+ _Ty __value;
+
+ STDEXEC_ATTRIBUTE((always_inline))
+ explicit __mbc(_Ty& __v) noexcept(
+ std::is_nothrow_move_constructible_v<_Ty>) :
+ __value((_Ty&&)__v)
+ {}
+
+ // This is a template so as to not be considered a copy/move constructor.
+ // Therefore, it doesn't suppress the generation of the default copy/move
+ // constructors.
+ STDEXEC_ATTRIBUTE((always_inline))
+ __mbc(same_as<__mbc> auto& __that) noexcept(
+ std::is_nothrow_move_constructible_v<_Ty>) :
+ __value(static_cast<_Ty&&>(__that.__value))
+ {}
+};
+
+// Rather strange definition of the lambda return type below is to reap the
+// benefits of SFINAE without nvc++ encoding the whole return type into the
+// symbol name.
+template <class _Ty>
+extern _Ty (*__f)();
+
+// Anonymous namespace here is to avoid symbol name collisions with the
+// lambda functions returned by __make_tuple.
+namespace
+{
+constexpr auto __make_tuple = //
+ []<class _Tag, class... _Captures>(_Tag, _Captures&&... __captures) {
+ return
+ [=]<class _Cvref, class _Fun>(_Cvref __cvref, _Fun&& __fun) mutable //
+ noexcept(
+ __nothrow_callable<_Fun, _Tag, __minvoke<_Captures, _Cvref>...>) //
+ -> decltype(__f<__call_result_t<_Fun, _Tag,
+ __minvoke<_Captures, _Cvref>...>>())
+ requires __callable<_Fun, _Tag, __minvoke<_Captures, _Cvref>...>
+ {
+ return ((_Fun&&)__fun)(
+ _Tag(),
+ const_cast<__minvoke<_Captures, _Cvref>&&>(__captures.__value)...);
+ };
+};
+} // anonymous namespace
+
+template <class _Tag>
+template <class _Data, class... _Children>
+constexpr auto __make_sexpr_t<_Tag>::operator()(_Data __data,
+ _Children... __children) const
+{
+ return __sexpr{__make_tuple(_Tag(), __detail::__mbc(__data),
+ __detail::__mbc(__children)...)};
+}
+#else
+// Anonymous namespace here is to avoid symbol name collisions with the
+// lambda functions returned by __make_tuple.
+namespace
+{
+constexpr auto __make_tuple = //
+ []<class _Tag, class... _Captures>(_Tag, _Captures&&... __captures) {
+ return
+ [... __captures = (_Captures&&)__captures]<class _Cvref, class _Fun>(
+ _Cvref, _Fun&& __fun) mutable //
+ noexcept(
+ __nothrow_callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>) //
+ -> __call_result_t<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
+ requires __callable<_Fun, _Tag, __minvoke<_Cvref, _Captures>...>
+ {
+ return ((_Fun&&)__fun)(
+ _Tag(), const_cast<__minvoke<_Cvref, _Captures>&&>(__captures)...);
+ };
+};
+} // anonymous namespace
+
+template <class _Tag>
+template <class _Data, class... _Children>
+constexpr auto __make_sexpr_t<_Tag>::operator()(_Data __data,
+ _Children... __children) const
+{
+ return __sexpr{
+ __make_tuple(_Tag(), (_Data&&)__data, (_Children&&)__children...)};
+};
+#endif
+
+template <class _Tag>
+inline constexpr __make_sexpr_t<_Tag> __make_sexpr{};
+} // namespace __detail
+
+using __detail::__make_sexpr;
+
+template <class _Tag, class _Data, class... _Children>
+using __sexpr_t = __result_of<__make_sexpr<_Tag>, _Data, _Children...>;
+
+namespace __detail
+{
+struct __sexpr_apply_t
+{
+ template <class _Sender, class _ApplyFn>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ auto operator()(_Sender&& __sndr, _ApplyFn&& __fun) const //
+ noexcept(noexcept(STDEXEC_CALL_EXPLICIT_THIS_MEMFN(
+ ((_Sender&&)__sndr), apply)((_ApplyFn&&)__fun))) //
+ -> decltype(STDEXEC_CALL_EXPLICIT_THIS_MEMFN(((_Sender&&)__sndr),
+ apply)((_ApplyFn&&)__fun))
+ {
+ return STDEXEC_CALL_EXPLICIT_THIS_MEMFN(((_Sender&&)__sndr),
+ apply)((_ApplyFn&&)__fun); //
+ }
+};
+} // namespace __detail
+
+using __detail::__sexpr_apply_t;
+inline constexpr __sexpr_apply_t __sexpr_apply{};
+
+template <class _Sender, class _ApplyFn>
+using __sexpr_apply_result_t =
+ __call_result_t<__sexpr_apply_t, _Sender, _ApplyFn>;
+
+namespace __detail
+{
+template <class _Sender>
+using __meta_of =
+ __call_result_t<__sexpr_apply_t, _Sender, __detail::__get_meta>;
+}
+
+template <class _Sender>
+using __tag_of = typename __detail::__meta_of<_Sender>::__tag;
+
+template <class _Sender>
+using __data_of = typename __detail::__meta_of<_Sender>::__data;
+
+template <class _Sender, class _Continuation = __q<__types>>
+using __children_of = //
+ __mapply<_Continuation, typename __detail::__meta_of<_Sender>::__children>;
+
+template <class _Ny, class _Sender>
+using __nth_child_of = __children_of<_Sender, __mbind_front_q<__m_at, _Ny>>;
+
+template <std::size_t _Ny, class _Sender>
+using __nth_child_of_c =
+ __children_of<_Sender, __mbind_front_q<__m_at, __msize_t<_Ny>>>;
+
+template <class _Sender>
+using __child_of = __children_of<_Sender, __q<__mfront>>;
+
+template <class _Sender>
+inline constexpr std::size_t __nbr_children_of =
+ __v<__children_of<_Sender, __msize>>;
+
+template <class _Sender>
+concept sender_expr = //
+ __mvalid<__tag_of, _Sender>;
+
+template <class _Sender, class _Tag>
+concept sender_expr_for = //
+ sender_expr<_Sender> && same_as<__tag_of<_Sender>, _Tag>;
+
+// The __name_of utility defined below is used to pretty-print the type names of
+// senders in compiler diagnostics.
+namespace __detail
+{
+template <class _Sender>
+extern __q<__midentity> __name_of_v;
+
+template <class _Sender>
+using __name_of_fn = decltype(__name_of_v<_Sender>);
+
+template <class _Sender>
+using __name_of = __minvoke<__name_of_fn<_Sender>, _Sender>;
+
+struct __basic_sender_name
+{
+ template <class _Sender>
+ using __f = //
+ __call_result_t<__sexpr_apply_result_t<_Sender, __basic_sender_name>>;
+
+ template <class _Tag, class _Data, class... _Children>
+ auto operator()(_Tag, _Data&&, _Children&&...) const //
+ -> __sexpr<_Tag, _Data, __name_of<_Children>...> (*)();
+};
+
+struct __id_name
+{
+ template <class _Sender>
+ using __f = __name_of<__id<_Sender>>;
+};
+
+template <class _Sender>
+extern __mcompose<__cplr, __name_of_fn<_Sender>> __name_of_v<_Sender&>;
+
+template <class _Sender>
+extern __mcompose<__cprr, __name_of_fn<_Sender>> __name_of_v<_Sender&&>;
+
+template <class _Sender>
+extern __mcompose<__cpclr, __name_of_fn<_Sender>> __name_of_v<const _Sender&>;
+
+template <class _Impl>
+extern __basic_sender_name __name_of_v<__sexpr<_Impl>>;
+
+template <__has_id _Sender>
+ requires(!same_as<__id<_Sender>, _Sender>)
+extern __id_name __name_of_v<_Sender>;
+
+template <class _Ty>
+_Ty __remove_rvalue_reference_fn(_Ty&&);
+
+template <class _Ty>
+using __remove_rvalue_reference_t =
+ decltype(__detail::__remove_rvalue_reference_fn(__declval<_Ty>()));
+} // namespace __detail
+
+template <class _Sender>
+using __name_of = __detail::__name_of<_Sender>;
+} // namespace stdexec
+
+namespace std
+{
+template <class _Impl>
+struct tuple_size<stdexec::__sexpr<_Impl>> :
+ integral_constant<
+ size_t, stdexec::__v<typename stdexec::__sexpr<_Impl>::__arity_t> + 2>
+{};
+
+template <size_t _Idx, class _Impl>
+struct tuple_element<_Idx, stdexec::__sexpr<_Impl>>
+{
+ using type =
+ stdexec::__detail::__remove_rvalue_reference_t<stdexec::__call_result_t<
+ _Impl, stdexec::__cp, stdexec::__nth_pack_element_t<_Idx>>>;
+};
+} // namespace std
diff --git a/include/sdbusplus/async/stdexec/__detail/__config.hpp b/include/sdbusplus/async/stdexec/__detail/__config.hpp
index 8772163..7cfd3dc 100644
--- a/include/sdbusplus/async/stdexec/__detail/__config.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__config.hpp
@@ -19,13 +19,23 @@
#error This library requires the use of C++20.
#endif
+#if __has_include(<version>)
+#include <version>
+#else
+#include <ciso646> // For stdlib feature-test macros when <version> is not available
+#endif
+
#include <cassert>
+#include <version>
+
+#define STDEXEC_STRINGIZE(_ARG) #_ARG
#define STDEXEC_CAT_(_XP, ...) _XP##__VA_ARGS__
#define STDEXEC_CAT(_XP, ...) STDEXEC_CAT_(_XP, __VA_ARGS__)
#define STDEXEC_EXPAND(...) __VA_ARGS__
#define STDEXEC_EVAL(_MACRO, ...) _MACRO(__VA_ARGS__)
+#define STDEXEC_EAT(...)
#define STDEXEC_NOT(_XP) STDEXEC_CAT(STDEXEC_NOT_, _XP)
#define STDEXEC_NOT_0 1
@@ -40,46 +50,184 @@
STDEXEC_EXPAND(STDEXEC_COUNT_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))
#define STDEXEC_COUNT_(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _NP, ...) _NP
-#define STDEXEC_CHECK(...) STDEXEC_EXPAND(STDEXEC_CHECK_N(__VA_ARGS__, 0, ))
-#define STDEXEC_CHECK_N(_XP, _NP, ...) _NP
-#define STDEXEC_PROBE(_XP) _XP, 1,
+#define STDEXEC_CHECK(...) STDEXEC_EXPAND(STDEXEC_CHECK_(__VA_ARGS__, 0, ))
+#define STDEXEC_CHECK_(_XP, _NP, ...) _NP
+#define STDEXEC_PROBE(...) STDEXEC_PROBE_(__VA_ARGS__, 1)
+#define STDEXEC_PROBE_(_XP, _NP, ...) _XP, _NP,
-#if defined(__NVCOMPILER)
-#define STDEXEC_NVHPC() 1
+////////////////////////////////////////////////////////////////////////////////
+// STDEXEC_FOR_EACH
+// Inspired by "Recursive macros with C++20 __VA_OPT__", by David Mazières
+// https://www.scs.stanford.edu/~dm/blog/va-opt.html
+#define STDEXEC_EXPAND_R(...) \
+ STDEXEC_EXPAND_R1( \
+ STDEXEC_EXPAND_R1(STDEXEC_EXPAND_R1(STDEXEC_EXPAND_R1(__VA_ARGS__)))) \
+ /**/
+#define STDEXEC_EXPAND_R1(...) \
+ STDEXEC_EXPAND_R2( \
+ STDEXEC_EXPAND_R2(STDEXEC_EXPAND_R2(STDEXEC_EXPAND_R2(__VA_ARGS__)))) \
+ /**/
+#define STDEXEC_EXPAND_R2(...) \
+ STDEXEC_EXPAND_R3( \
+ STDEXEC_EXPAND_R3(STDEXEC_EXPAND_R3(STDEXEC_EXPAND_R3(__VA_ARGS__)))) \
+ /**/
+#define STDEXEC_EXPAND_R3(...) \
+ STDEXEC_EXPAND( \
+ STDEXEC_EXPAND(STDEXEC_EXPAND(STDEXEC_EXPAND(__VA_ARGS__)))) \
+ /**/
+
+#define STDEXEC_PARENS ()
+#define STDEXEC_FOR_EACH(_MACRO, ...) \
+ __VA_OPT__(STDEXEC_EXPAND_R(STDEXEC_FOR_EACH_HELPER(_MACRO, __VA_ARGS__))) \
+ /**/
+#define STDEXEC_FOR_EACH_HELPER(_MACRO, _A1, ...) \
+ _MACRO(_A1) \
+ __VA_OPT__(STDEXEC_FOR_EACH_AGAIN STDEXEC_PARENS(_MACRO, __VA_ARGS__)) /**/
+#define STDEXEC_FOR_EACH_AGAIN() STDEXEC_FOR_EACH_HELPER
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// If tail is non-empty, expand to the tail. Otherwise, expand to the head
+#define STDEXEC_HEAD_OR_TAIL(_XP, ...) \
+ STDEXEC_EXPAND __VA_OPT__((__VA_ARGS__)STDEXEC_EAT)(_XP)
+
+// If tail is non-empty, expand to nothing. Otherwise, expand to the head
+#define STDEXEC_HEAD_OR_NULL(_XP, ...) \
+ STDEXEC_EXPAND __VA_OPT__(() STDEXEC_EAT)(_XP)
+
+// When used with no arguments, these macros expand to 1 if the current
+// compiler corresponds to the macro name; 0, otherwise. When used with
+// arguments, they expand to the arguments if if the current compiler
+// corresponds to the macro name; nothing, otherwise.
+#if defined(__NVCC__)
+#define STDEXEC_NVCC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#elif defined(__NVCOMPILER)
+#define STDEXEC_NVHPC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#elif defined(__EDG__)
+#define STDEXEC_EDG(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#elif defined(__clang__)
-#define STDEXEC_CLANG() 1
+#define STDEXEC_CLANG(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#if defined(_MSC_VER)
+#define STDEXEC_CLANG_CL(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#endif
#elif defined(__GNUC__)
-#define STDEXEC_GCC() 1
+#define STDEXEC_GCC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#elif defined(_MSC_VER)
-#define STDEXEC_MSVC() 1
+#define STDEXEC_MSVC(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
#endif
+#ifndef STDEXEC_NVCC
+#define STDEXEC_NVCC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
+#endif
#ifndef STDEXEC_NVHPC
-#define STDEXEC_NVHPC() 0
+#define STDEXEC_NVHPC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
+#endif
+#ifndef STDEXEC_EDG
+#define STDEXEC_EDG(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
#ifndef STDEXEC_CLANG
-#define STDEXEC_CLANG() 0
+#define STDEXEC_CLANG(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
+#endif
+#ifndef STDEXEC_CLANG_CL
+#define STDEXEC_CLANG_CL(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
#ifndef STDEXEC_GCC
-#define STDEXEC_GCC() 0
+#define STDEXEC_GCC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
#ifndef STDEXEC_MSVC
-#define STDEXEC_MSVC() 0
+#define STDEXEC_MSVC(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
#endif
-#if STDEXEC_CLANG()
-#define STDEXEC_STRINGIZE(_ARG) #_ARG
+////////////////////////////////////////////////////////////////////////////////////////////////////
+#ifdef __CUDACC__
+#define STDEXEC_CUDA(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#else
+#define STDEXEC_CUDA(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// For portably declaring attributes on functions and types
+// Usage:
+//
+// STDEXEC_ATTRIBUTE((attr1, attr2, ...))
+// void foo() { ... }
+#define STDEXEC_ATTRIBUTE(_XP) \
+ STDEXEC_FOR_EACH(STDEXEC_ATTR, STDEXEC_EXPAND _XP)
+#define STDEXEC_ATTR(_ATTR) \
+ STDEXEC_CAT(STDEXEC_ATTR_WHICH_, \
+ STDEXEC_CHECK(STDEXEC_CAT(STDEXEC_ATTR_, _ATTR))) \
+ (_ATTR)
+
+// unknown attributes are treated like C++-style attributes
+#define STDEXEC_ATTR_WHICH_0(_ATTR) [[_ATTR]]
+
+// custom handling for specific attribute types
+#define STDEXEC_ATTR_WHICH_1(_ATTR) STDEXEC_CUDA(__host__)
+#define STDEXEC_ATTR_host STDEXEC_PROBE(~, 1)
+#define STDEXEC_ATTR___host__ STDEXEC_PROBE(~, 1)
+
+#define STDEXEC_ATTR_WHICH_2(_ATTR) STDEXEC_CUDA(__device__)
+#define STDEXEC_ATTR_device STDEXEC_PROBE(~, 2)
+#define STDEXEC_ATTR___device__ STDEXEC_PROBE(~, 2)
+
+#if STDEXEC_NVHPC()
+// NVBUG #4067067: NVHPC does not fully support [[no_unique_address]]
+#define STDEXEC_ATTR_WHICH_3(_ATTR) /*nothing*/
+#elif STDEXEC_MSVC()
+// MSVCBUG
+// https://developercommunity.visualstudio.com/t/Incorrect-codegen-when-using-msvc::no_/10452874
+#define STDEXEC_ATTR_WHICH_3(_ATTR) // [[msvc::no_unique_address]]
+#elif STDEXEC_CLANG_CL()
+// clang-cl does not support: https://reviews.llvm.org/D110485
+#define STDEXEC_ATTR_WHICH_3(_ATTR) // [[msvc::no_unique_address]]
+#else
+#define STDEXEC_ATTR_WHICH_3(_ATTR) [[no_unique_address]]
+#endif
+#define STDEXEC_ATTR_no_unique_address STDEXEC_PROBE(~, 3)
+
+#if STDEXEC_MSVC()
+#define STDEXEC_ATTR_WHICH_4(_ATTR) __forceinline
+#elif STDEXEC_CLANG()
+#define STDEXEC_ATTR_WHICH_4(_ATTR) \
+ __attribute__((__always_inline__, __artificial__, __nodebug__)) inline
+#elif defined(__GNUC__)
+#define STDEXEC_ATTR_WHICH_4(_ATTR) \
+ __attribute__((__always_inline__, __artificial__)) inline
+#else
+#define STDEXEC_ATTR_WHICH_4(_ATTR) /*nothing*/
+#endif
+#define STDEXEC_ATTR_always_inline STDEXEC_PROBE(~, 4)
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// warning push/pop portability macros
+#if STDEXEC_NVCC()
+#define STDEXEC_PRAGMA_PUSH() _Pragma("nv_diagnostic push")
+#define STDEXEC_PRAGMA_POP() _Pragma("nv_diagnostic pop")
+#define STDEXEC_PRAGMA_IGNORE_EDG(...) \
+ _Pragma(STDEXEC_STRINGIZE(nv_diag_suppress __VA_ARGS__))
+#elif STDEXEC_NVHPC() || STDEXEC_EDG()
+#define STDEXEC_PRAGMA_PUSH() \
+ _Pragma("diagnostic push") STDEXEC_PRAGMA_IGNORE_EDG(invalid_error_number)
+#define STDEXEC_PRAGMA_POP() _Pragma("diagnostic pop")
+#define STDEXEC_PRAGMA_IGNORE_EDG(...) \
+ _Pragma(STDEXEC_STRINGIZE(diag_suppress __VA_ARGS__))
+#elif STDEXEC_CLANG() || STDEXEC_GCC()
#define STDEXEC_PRAGMA_PUSH() _Pragma("GCC diagnostic push")
#define STDEXEC_PRAGMA_POP() _Pragma("GCC diagnostic pop")
-#define STDEXEC_PRAGMA_IGNORE(_ARG) \
- _Pragma(STDEXEC_STRINGIZE(GCC diagnostic ignored _ARG))
+#define STDEXEC_PRAGMA_IGNORE_GNU(...) \
+ _Pragma(STDEXEC_STRINGIZE(GCC diagnostic ignored __VA_ARGS__))
#else
#define STDEXEC_PRAGMA_PUSH()
#define STDEXEC_PRAGMA_POP()
-#define STDEXEC_PRAGMA_IGNORE(_ARG)
#endif
-#ifdef __has_builtin
+#ifndef STDEXEC_PRAGMA_IGNORE_GNU
+#define STDEXEC_PRAGMA_IGNORE_GNU(...)
+#endif
+#ifndef STDEXEC_PRAGMA_IGNORE_EDG
+#define STDEXEC_PRAGMA_IGNORE_EDG(...)
+#endif
+
+#if !STDEXEC_MSVC() && defined(__has_builtin)
#define STDEXEC_HAS_BUILTIN __has_builtin
#else
#define STDEXEC_HAS_BUILTIN(...) 0
@@ -92,7 +240,7 @@
std::is_trivially_copyable_v<__VA_ARGS__>
#endif
-#if STDEXEC_HAS_BUILTIN(__is_base_of) || STDEXEC_MSVC()
+#if STDEXEC_HAS_BUILTIN(__is_base_of) || (_MSC_VER >= 1914)
#define STDEXEC_IS_BASE_OF(...) __is_base_of(__VA_ARGS__)
#else
#define STDEXEC_IS_BASE_OF(...) std::is_base_of_v<__VA_ARGS__>
@@ -106,6 +254,22 @@
#define STDEXEC_IS_CONVERTIBLE_TO(...) std::is_convertible_v<__VA_ARGS__>
#endif
+#if STDEXEC_HAS_BUILTIN(__is_const)
+#define STDEXEC_IS_CONST(...) __is_const(__VA_ARGS__)
+#else
+#define STDEXEC_IS_CONST(...) stdexec::__is_const<__VA_ARGS__>
+#endif
+
+#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable >= 202202L
+#define STDEXEC_UNREACHABLE() std::unreachable()
+#elif STDEXEC_HAS_BUILTIN(__builtin_unreachable)
+#define STDEXEC_UNREACHABLE() __builtin_unreachable()
+#elif STDEXEC_MSVC()
+#define STDEXEC_UNREACHABLE(...) __assume(false)
+#else
+#define STDEXEC_UNREACHABLE(...) std::terminate()
+#endif
+
// Before gcc-12, gcc really didn't like tuples or variants of immovable types
#if STDEXEC_GCC() && (__GNUC__ < 12)
#define STDEXEC_IMMOVABLE(_XP) _XP(_XP&&)
@@ -113,33 +277,21 @@
#define STDEXEC_IMMOVABLE(_XP) _XP(_XP&&) = delete
#endif
-// NVBUG #4067067
-#if STDEXEC_NVHPC()
-#define STDEXEC_NO_UNIQUE_ADDRESS
-#else
-#define STDEXEC_NO_UNIQUE_ADDRESS [[no_unique_address]]
-#endif
-
// BUG (gcc PR93711): copy elision fails when initializing a
// [[no_unique_address]] field from a function returning an object
// of class type by value
#if STDEXEC_GCC()
#define STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
#else
-#define STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS [[no_unique_address]]
-#endif
-
-#if STDEXEC_CLANG() && defined(__CUDACC__)
-#define STDEXEC_DETAIL_CUDACC_HOST_DEVICE __host__ __device__
-#else
-#define STDEXEC_DETAIL_CUDACC_HOST_DEVICE
+#define STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS \
+ STDEXEC_ATTRIBUTE((no_unique_address))
#endif
#if STDEXEC_NVHPC()
#include <nv/target>
#define STDEXEC_TERMINATE() \
NV_IF_TARGET(NV_IS_HOST, (std::terminate();), (__trap();)) void()
-#elif STDEXEC_CLANG() && defined(__CUDACC__) && defined(__CUDA_ARCH__)
+#elif STDEXEC_CLANG() && STDEXEC_CUDA() && defined(__CUDA_ARCH__)
#define STDEXEC_TERMINATE() \
__trap(); \
__builtin_unreachable()
@@ -147,6 +299,22 @@
#define STDEXEC_TERMINATE() std::terminate()
#endif
+// Before clang-16, clang did not like libstdc++'s ranges implementation
+#if __has_include(<ranges>) && \
+ (defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L) && \
+ (!STDEXEC_CLANG() || __clang_major__ >= 16 || defined(_LIBCPP_VERSION))
+#define STDEXEC_HAS_STD_RANGES() 1
+#else
+#define STDEXEC_HAS_STD_RANGES() 0
+#endif
+
+#if __has_include(<memory_resource>) && \
+ (defined(__cpp_lib_memory_resource) && __cpp_lib_memory_resource >= 201603L)
+#define STDEXEC_HAS_STD_MEMORY_RESOURCE() 1
+#else
+#define STDEXEC_HAS_STD_MEMORY_RESOURCE() 0
+#endif
+
#ifdef STDEXEC_ASSERT
#error \
"Redefinition of STDEXEC_ASSERT is not permitted. Define STDEXEC_ASSERT_FN instead."
@@ -163,5 +331,68 @@
#define STDEXEC_ASSERT_FN assert
#endif
+#define STDEXEC_AUTO_RETURN(...) \
+ noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) \
+ { \
+ return __VA_ARGS__; \
+ }
+
+// GCC 13 implements lexical friendship, but it is incomplete. See
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111018
+#if STDEXEC_CLANG() // || (STDEXEC_GCC() && __GNUC__ >= 13)
+#define STDEXEC_FRIENDSHIP_IS_LEXICAL() 1
+#else
+#define STDEXEC_FRIENDSHIP_IS_LEXICAL() 0
+#endif
+
+#if defined(__cpp_explicit_this_parameter) && \
+ (__cpp_explicit_this_parameter >= 202110)
+#define STDEXEC_EXPLICIT_THIS(...) STDEXEC_HEAD_OR_TAIL(1, __VA_ARGS__)
+#else
+#define STDEXEC_EXPLICIT_THIS(...) STDEXEC_HEAD_OR_NULL(0, __VA_ARGS__)
+#endif
+
+#if STDEXEC_EXPLICIT_THIS()
+#define STDEXEC_DEFINE_EXPLICIT_THIS_MEMFN(...) __VA_ARGS__
+#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN(_OBJ, _NAME) (_OBJ)._NAME( STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL
+#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL(...) __VA_ARGS__ )
+#else
+#define STDEXEC_DEFINE_EXPLICIT_THIS_MEMFN(...) static __VA_ARGS__(STDEXEC_FUN_ARGS
+#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN(_OBJ, _NAME) (_OBJ)._NAME((_OBJ) STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL
+#define STDEXEC_CALL_EXPLICIT_THIS_MEMFN_DETAIL(...) __VA_OPT__(, ) __VA_ARGS__)
+#define STDEXEC_EAT_THIS_DETAIL_this
+#define STDEXEC_FUN_ARGS(...) STDEXEC_CAT(STDEXEC_EAT_THIS_DETAIL_, __VA_ARGS__))
+#endif
+
+// Configure extra type checking
+#define STDEXEC_TYPE_CHECKING_ZERO() 0
+#define STDEXEC_TYPE_CHECKING_ONE() 1
+#define STDEXEC_TYPE_CHECKING_TWO() 2
+
+#define STDEXEC_PROBE_TYPE_CHECKING_ STDEXEC_TYPE_CHECKING_ONE
+#define STDEXEC_PROBE_TYPE_CHECKING_0 STDEXEC_TYPE_CHECKING_ZERO
+#define STDEXEC_PROBE_TYPE_CHECKING_1 STDEXEC_TYPE_CHECKING_ONE
+#define STDEXEC_PROBE_TYPE_CHECKING_STDEXEC_ENABLE_EXTRA_TYPE_CHECKING \
+ STDEXEC_TYPE_CHECKING_TWO
+
+#define STDEXEC_TYPE_CHECKING_WHICH3(...) \
+ STDEXEC_PROBE_TYPE_CHECKING_##__VA_ARGS__
+#define STDEXEC_TYPE_CHECKING_WHICH2(...) \
+ STDEXEC_TYPE_CHECKING_WHICH3(__VA_ARGS__)
+#define STDEXEC_TYPE_CHECKING_WHICH \
+ STDEXEC_TYPE_CHECKING_WHICH2(STDEXEC_ENABLE_EXTRA_TYPE_CHECKING)
+
+#ifndef STDEXEC_ENABLE_EXTRA_TYPE_CHECKING
+#define STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() 0
+#elif STDEXEC_TYPE_CHECKING_WHICH() == 2
+// do nothing
+#elif STDEXEC_TYPE_CHECKING_WHICH() == 0
+#undef STDEXEC_ENABLE_EXTRA_TYPE_CHECKING
+#define STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() 0
+#else
+#undef STDEXEC_ENABLE_EXTRA_TYPE_CHECKING
+#define STDEXEC_ENABLE_EXTRA_TYPE_CHECKING() 1
+#endif
+
namespace stdexec
{}
diff --git a/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp b/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp
index 31148a4..898c636 100644
--- a/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__execution_fwd.hpp
@@ -21,6 +21,10 @@
namespace stdexec
{
+struct __none_such;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+struct default_domain;
//////////////////////////////////////////////////////////////////////////////////////////////////
namespace __receivers
@@ -48,8 +52,10 @@
namespace __env
{
struct get_env_t;
-}
+struct empty_env;
+} // namespace __env
+using __env::empty_env;
using __env::get_env_t;
extern const get_env_t get_env;
@@ -119,10 +125,6 @@
using __get_completion_signatures::get_completion_signatures_t;
extern const get_completion_signatures_t get_completion_signatures;
-template <class _Sender, class _Env>
-using __completion_signatures_of_t =
- __call_result_t<get_completion_signatures_t, _Sender, _Env>;
-
//////////////////////////////////////////////////////////////////////////////////////////////////
namespace __connect
{
@@ -169,4 +171,41 @@
using __as_awaitable::as_awaitable_t;
extern const as_awaitable_t as_awaitable;
+//////////////////////////////////////////////////////////////////////////////////////////////////
+namespace __transfer
+{
+struct transfer_t;
+}
+
+using __transfer::transfer_t;
+extern const transfer_t transfer;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+namespace __transfer_just
+{
+struct transfer_just_t;
+}
+
+using __transfer_just::transfer_just_t;
+extern const transfer_just_t transfer_just;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+namespace __bulk
+{
+struct bulk_t;
+}
+
+using __bulk::bulk_t;
+extern const bulk_t bulk;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+namespace __on_v2
+{
+struct on_t;
+}
+
+namespace v2
+{
+using __on_v2::on_t;
+}
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__intrusive_queue.hpp b/include/sdbusplus/async/stdexec/__detail/__intrusive_queue.hpp
index edd0e88..2ea46b4 100644
--- a/include/sdbusplus/async/stdexec/__detail/__intrusive_queue.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__intrusive_queue.hpp
@@ -40,6 +40,10 @@
__tail_(std::exchange(__other.__tail_, nullptr))
{}
+ __intrusive_queue(_Item* __head, _Item* __tail) noexcept :
+ __head_(__head), __tail_(__tail)
+ {}
+
__intrusive_queue& operator=(__intrusive_queue __other) noexcept
{
std::swap(__head_, __other.__head_);
@@ -70,11 +74,33 @@
return __result;
}
+ static __intrusive_queue make(_Item* __list) noexcept
+ {
+ __intrusive_queue __result{};
+ __result.__head_ = __list;
+ __result.__tail_ = __list;
+ if (__list == nullptr)
+ {
+ return __result;
+ }
+ while (__result.__tail_->*_Next != nullptr)
+ {
+ __result.__tail_ = __result.__tail_->*_Next;
+ }
+ return __result;
+ }
+
[[nodiscard]] bool empty() const noexcept
{
return __head_ == nullptr;
}
+ void clear() noexcept
+ {
+ __head_ = nullptr;
+ __tail_ = nullptr;
+ }
+
[[nodiscard]] _Item* pop_front() noexcept
{
STDEXEC_ASSERT(!empty());
@@ -147,6 +173,111 @@
__other.__head_ = nullptr;
}
+ struct iterator
+ {
+ using difference_type = std::ptrdiff_t;
+ using value_type = _Item*;
+
+ _Item* __predecessor_ = nullptr;
+ _Item* __item_ = nullptr;
+
+ iterator() noexcept = default;
+
+ explicit iterator(_Item* __pred, _Item* __item) noexcept :
+ __predecessor_(__pred), __item_(__item)
+ {}
+
+ [[nodiscard]] _Item* operator*() const noexcept
+ {
+ STDEXEC_ASSERT(__item_ != nullptr);
+ return __item_;
+ }
+
+ [[nodiscard]] _Item** operator->() const noexcept
+ {
+ STDEXEC_ASSERT(__item_ != nullptr);
+ return &__item_;
+ }
+
+ iterator& operator++() noexcept
+ {
+ __predecessor_ = __item_;
+ if (__item_)
+ {
+ __item_ = __item_->*_Next;
+ }
+ return *this;
+ }
+
+ iterator operator++(int) noexcept
+ {
+ iterator __result = *this;
+ ++*this;
+ return __result;
+ }
+
+ friend bool operator==(const iterator&,
+ const iterator&) noexcept = default;
+ };
+
+ [[nodiscard]] iterator begin() const noexcept
+ {
+ return iterator(nullptr, __head_);
+ }
+
+ [[nodiscard]] iterator end() const noexcept
+ {
+ return iterator(__tail_, nullptr);
+ }
+
+ void splice(iterator pos, __intrusive_queue& other, iterator first,
+ iterator last) noexcept
+ {
+ if (first == last)
+ {
+ return;
+ }
+ STDEXEC_ASSERT(first.__item_ != nullptr);
+ STDEXEC_ASSERT(last.__predecessor_ != nullptr);
+ if (other.__head_ == first.__item_)
+ {
+ other.__head_ = last.__item_;
+ if (other.__head_ == nullptr)
+ {
+ other.__tail_ = nullptr;
+ }
+ }
+ else
+ {
+ STDEXEC_ASSERT(first.__predecessor_ != nullptr);
+ first.__predecessor_->*_Next = last.__item_;
+ last.__predecessor_->*_Next = pos.__item_;
+ }
+ if (empty())
+ {
+ __head_ = first.__item_;
+ __tail_ = last.__predecessor_;
+ }
+ else
+ {
+ pos.__predecessor_->*_Next = first.__item_;
+ if (pos.__item_ == nullptr)
+ {
+ __tail_ = last.__predecessor_;
+ }
+ }
+ }
+
+ _Item* front() const noexcept
+ {
+ return __head_;
+ }
+
+ _Item* back() const noexcept
+ {
+ return __tail_;
+ }
+
private:
_Item* __head_ = nullptr;
_Item* __tail_ = nullptr;
diff --git a/include/sdbusplus/async/stdexec/__detail/__meta.hpp b/include/sdbusplus/async/stdexec/__detail/__meta.hpp
index 698526e..d1f5fea 100644
--- a/include/sdbusplus/async/stdexec/__detail/__meta.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__meta.hpp
@@ -37,6 +37,7 @@
{
__ignore() = default;
+ STDEXEC_ATTRIBUTE((always_inline)) //
constexpr __ignore(auto&&...) noexcept {}
};
@@ -89,8 +90,8 @@
template <class _Tp, class _Up>
using __mfirst = _Tp;
-template <class _Tp, class _UXp>
-using __msecond = _UXp;
+template <class _Tp, class _Up>
+using __msecond = _Up;
template <class _Tp>
extern const __undefined<_Tp> __v;
@@ -117,6 +118,9 @@
template <std::size_t _Np>
using __make_indices = std::make_index_sequence<_Np>*;
+template <class... _Ts>
+using __indices_for = __make_indices<sizeof...(_Ts)>;
+
template <class _Char>
concept __mchar = __same_as<_Char, char>;
@@ -143,6 +147,17 @@
return _Len;
}
+ template <std::size_t... _Is>
+ constexpr bool __equal(__mstring __other, __indices<_Is...>) const noexcept
+ {
+ return ((__what_[_Is] == __other.__what_[_Is]) && ...);
+ }
+
+ constexpr bool operator==(__mstring __other) const noexcept
+ {
+ return __equal(__other, __make_indices<_Len>());
+ }
+
const char __what_[_Len];
};
@@ -153,10 +168,10 @@
{
return {_Str...};
}
-#elif STDEXEC_NVHPC()
-// BUGBUG TODO This is to work around an unknown EDG bug
+#elif STDEXEC_NVHPC() && (__EDG_VERSION__ < 605)
+// This is to work around an unfiled (by me) EDG bug that fixed in build 605
template <__mstring _Str>
-constexpr auto operator""__csz() noexcept
+constexpr const __mtypeof<_Str> operator""__csz() noexcept
{
return _Str;
}
@@ -178,11 +193,15 @@
template <class _What, class... _With>
struct _ERROR_
{
- const _ERROR_& operator,(__msuccess) const noexcept;
+ _ERROR_ operator,(__msuccess) const noexcept;
};
+template <__mstring _What>
+struct _WHAT_
+{};
+
template <class _What, class... _With>
-using __mexception = const _ERROR_<_What, _With...>&;
+using __mexception = _ERROR_<_What, _With...>;
template <class>
extern __msuccess __ok_v;
@@ -194,21 +213,7 @@
using __ok_t = decltype(__ok_v<_Ty>);
template <class... _Ts>
-using __disp = const decltype((__msuccess(), ..., __ok_t<_Ts>()))&;
-
-template <bool _AllOK>
-struct __i
-{
- template <template <class...> class _Fn, class... _Args>
- using __g = _Fn<_Args...>;
-};
-
-template <>
-struct __i<false>
-{
- template <template <class...> class, class... _Args>
- using __g = __disp<_Args...>;
-};
+using __disp = decltype((__msuccess(), ..., __ok_t<_Ts>()));
template <class _Arg>
concept __ok = __same_as<__ok_t<_Arg>, __msuccess>;
@@ -219,6 +224,9 @@
template <class... _Args>
concept _Ok = (__ok<_Args> && ...);
+template <bool _AllOK>
+struct __i;
+
#if STDEXEC_NVHPC()
// Most compilers memoize alias template specializations, but
// nvc++ does not. So we memoize the type computations by
@@ -240,15 +248,52 @@
template <template <class...> class _Fn, class... _Args>
using __meval = __t<__meval_<_Fn, _Args...>>;
+template <class _Fn, class... _Args>
+using __minvoke__ = typename __i<_Ok<_Fn>>::template __h<_Fn, _Args...>;
+
+template <class _Fn, class... _Args>
+struct __minvoke_
+{};
+
+template <class _Fn, class... _Args>
+ requires __typename<__minvoke__<_Fn, _Args...>>
+struct __minvoke_<_Fn, _Args...>
+{
+ using __t = __minvoke__<_Fn, _Args...>;
+};
+
+template <class _Fn, class... _Args>
+using __minvoke = __t<__minvoke_<_Fn, _Args...>>;
+
#else
template <template <class...> class _Fn, class... _Args>
using __meval = typename __i<_Ok<_Args...>>::template __g<_Fn, _Args...>;
+template <class _Fn, class... _Args>
+using __minvoke = typename __i<_Ok<_Fn>>::template __h<_Fn, _Args...>;
+
#endif
-template <class _Fn, class... _Args>
-using __minvoke = __meval<_Fn::template __f, _Args...>;
+template <bool _AllOK>
+struct __i
+{
+ template <template <class...> class _Fn, class... _Args>
+ using __g = _Fn<_Args...>;
+
+ template <class _Fn, class... _Args>
+ using __h = __meval<_Fn::template __f, _Args...>;
+};
+
+template <>
+struct __i<false>
+{
+ template <template <class...> class, class... _Args>
+ using __g = __disp<_Args...>;
+
+ template <class _Fn, class...>
+ using __h = _Fn;
+};
template <template <class...> class _Fn>
struct __q
@@ -278,13 +323,13 @@
using __mbind_back = __mbind_back_q<_Fn::template __f, _Back...>;
template <template <class...> class _Tp, class... _Args>
-concept __valid = requires { typename __meval<_Tp, _Args...>; };
+concept __mvalid = requires { typename __meval<_Tp, _Args...>; };
template <class _Fn, class... _Args>
-concept __minvocable = __valid<_Fn::template __f, _Args...>;
+concept __minvocable = __mvalid<_Fn::template __f, _Args...>;
template <template <class...> class _Tp, class... _Args>
-concept __msucceeds = __valid<_Tp, _Args...> && __ok<__meval<_Tp, _Args...>>;
+concept __msucceeds = __mvalid<_Tp, _Args...> && __ok<__meval<_Tp, _Args...>>;
template <class _Fn, class... _Args>
concept __minvocable_succeeds = __minvocable<_Fn, _Args...> &&
@@ -313,6 +358,12 @@
struct __mdefer : __mdefer_<_Fn, _Args...>
{};
+template <class _Fn, class... _Args>
+using __mmemoize = __t<__mdefer<_Fn, _Args...>>;
+
+template <template <class...> class _Fn, class... _Args>
+using __mmemoize_q = __mmemoize<__q<_Fn>, _Args...>;
+
struct __if_
{
template <bool>
@@ -341,6 +392,12 @@
requires(sizeof...(_False) <= 1)
using __if_c = __minvoke<__if_::__<_Pred>, _True, _False...>;
+template <class _Pred, class _True, class... _False>
+using __minvoke_if = __minvoke<__if<_Pred, _True, _False...>>;
+
+template <bool _Pred, class _True, class... _False>
+using __minvoke_if_c = __minvoke<__if_c<_Pred, _True, _False...>>;
+
template <class _Tp>
struct __mconst
{
@@ -348,25 +405,6 @@
using __f = _Tp;
};
-template <template <class...> class _Try, class _Catch>
-struct __mtry_catch_q
-{
- template <class... _Args>
- using __f =
- __minvoke<__if_c<__valid<_Try, _Args...>, __q<_Try>, _Catch>, _Args...>;
-};
-
-template <class _Try, class _Catch>
-struct __mtry_catch
-{
- template <class... _Args>
- using __f =
- __minvoke<__if_c<__minvocable<_Try, _Args...>, _Try, _Catch>, _Args...>;
-};
-
-template <class _Fn, class _Default>
-using __with_default = __mtry_catch<_Fn, __mconst<_Default>>;
-
inline constexpr __mstring __mbad_substitution =
"The specified meta-function could not be evaluated with the types provided."__csz;
@@ -375,8 +413,7 @@
{};
template <class... _Args>
-struct _WITH_TYPES_
-{};
+struct _WITH_TYPES_;
template <template <class...> class _Fun>
struct _WITH_META_FUNCTION_T_
@@ -394,6 +431,34 @@
_WITH_TYPES_<_Args...>>;
};
+template <template <class...> class _Try, class _Catch>
+struct __mtry_catch_q
+{
+ template <class... _Args>
+ using __f = __minvoke<__if_c<__mvalid<_Try, _Args...>, __q<_Try>, _Catch>,
+ _Args...>;
+};
+
+template <class _Try, class _Catch>
+struct __mtry_catch
+{
+ template <class... _Args>
+ using __f =
+ __minvoke<__if_c<__minvocable<_Try, _Args...>, _Try, _Catch>, _Args...>;
+};
+
+template <class _Fn, class _Default>
+using __with_default = __mtry_catch<_Fn, __mconst<_Default>>;
+
+template <template <class...> class _Fn, class _Default>
+using __with_default_q = __mtry_catch_q<_Fn, __mconst<_Default>>;
+
+template <class _Fn, class _Default, class... _Args>
+using __minvoke_or = __minvoke<__with_default<_Fn, _Default>, _Args...>;
+
+template <template <class...> class _Fn, class _Default, class... _Args>
+using __meval_or = __minvoke<__with_default_q<_Fn, _Default>, _Args...>;
+
template <template <class...> class _Fn>
struct __mtry_eval_
{
@@ -657,8 +722,11 @@
template <class... _As>
requires(sizeof...(_As) == 1)
using __msingle = __mfront<_As...>;
-template <class _Ty>
-using __msingle_or = __mbind_back_q<__mfront_, _Ty>;
+template <class _Default, class... _As>
+ requires(sizeof...(_As) <= 1)
+using __msingle_or_ = __mfront<_As..., _Default>;
+template <class _Default>
+using __msingle_or = __mbind_front_q<__msingle_or_, _Default>;
template <class _Continuation = __q<__types>>
struct __pop_front
@@ -711,8 +779,8 @@
template <class _Ty>
using __id = __minvoke<__id_<__has_id<_Ty>>, _Ty>;
-template <class _Ty>
-using __cvref_t = __copy_cvref_t<_Ty, __t<std::remove_cvref_t<_Ty>>>;
+template <class _From, class _To = __decay_t<_From>>
+using __cvref_t = __copy_cvref_t<_From, __t<_To>>;
template <class _From, class _To = __decay_t<_From>>
using __cvref_id = __copy_cvref_t<_From, __id<_To>>;
@@ -730,6 +798,23 @@
using __call_result_t = decltype(__declval<_Fun>()(__declval<_As>()...));
#endif
+template <const auto& _Fun, class... _As>
+using __result_of = __call_result_t<decltype(_Fun), _As...>;
+
+#if STDEXEC_CLANG() && (__clang_major__ < 13)
+template <class _Ty>
+constexpr auto __hide_ = [] { return (__mtype<_Ty>(*)())0; };
+#else
+template <class _Ty>
+extern decltype([] { return (__mtype<_Ty>(*)())0; }) __hide_;
+#endif
+
+template <class _Ty>
+using __hide = decltype(__hide_<_Ty>);
+
+template <class _Id>
+using __unhide = __t<__call_result_t<__call_result_t<_Id>>>;
+
// For working around clang's lack of support for CWG#2369:
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2369
struct __qcall_result
@@ -888,9 +973,9 @@
using __f = __mor<__minvoke<_Fn, _Args>...>;
};
-#if __has_builtin(__type_pack_element)
+#if STDEXEC_HAS_BUILTIN(__type_pack_element)
template <std::size_t _Np, class... _Ts>
-using __m_at = __type_pack_element<_Np, _Ts...>;
+using __m_at_c = __type_pack_element<_Np, _Ts...>;
#else
template <std::size_t>
using __void_ptr = void*;
@@ -911,13 +996,46 @@
};
template <std::size_t _Np, class... _Ts>
-using __m_at = __minvoke<__m_at_<std::make_index_sequence<_Np>>, _Ts...>;
+using __m_at_c = __minvoke<__m_at_<std::make_index_sequence<_Np>>, _Ts...>;
#endif
+template <class _Np, class... _Ts>
+using __m_at = __m_at_c<__v<_Np>, _Ts...>;
+
+template <class... _Ts>
+using __mback = __m_at_c<sizeof...(_Ts) - 1, _Ts...>;
+
+template <class _Continuation = __q<__types>>
+struct __mpop_back
+{
+ template <class>
+ struct __impl;
+
+ template <std::size_t... _Idx>
+ struct __impl<__indices<_Idx...>>
+ {
+ template <class... _Ts>
+ using __f = __minvoke<_Continuation, __m_at_c<_Idx, _Ts...>...>;
+ };
+
+ template <class... _Ts>
+ requires(sizeof...(_Ts) != 0)
+ using __f = __minvoke<__impl<__make_indices<sizeof...(_Ts) - 1>>, _Ts...>;
+};
+
template <std::size_t _Np>
-struct __placeholder_;
-template <std::size_t _Np>
-using __placeholder = __placeholder_<_Np>*;
+struct __placeholder
+{
+ __placeholder() = default;
+
+ constexpr __placeholder(void*) noexcept {}
+
+ friend constexpr std::size_t
+ __get_placeholder_offset(__placeholder) noexcept
+ {
+ return _Np;
+ }
+};
using __0 = __placeholder<0>;
using __1 = __placeholder<1>;
@@ -939,25 +1057,78 @@
template <template <class...> class _Cp, class _Noexcept = __mbool<true>>
using __mconstructor_for = __mcompose<__q<__mconstruct>, __q<_Cp>>;
+#if STDEXEC_MSVC()
+// MSVCBUG
+// https://developercommunity.visualstudio.com/t/Incorrect-function-template-argument-sub/10437827
+
+template <std::size_t>
+struct __ignore_t
+{
+ __ignore_t() = default;
+
+ constexpr __ignore_t(auto&&...) noexcept {}
+};
+#else
template <std::size_t>
using __ignore_t = __ignore;
+#endif
-template <std::size_t... _Is, class _Ty, class... _Us>
-_Ty&& __nth_pack_element_(__ignore_t<_Is>..., _Ty&& __t, _Us&&...) noexcept
+template <class... _Ignore>
+struct __nth_pack_element_impl
{
- return (_Ty&&)__t;
-}
+ template <class _Ty, class... _Us>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr _Ty&& operator()(_Ignore..., _Ty&& __t, _Us&&...) const noexcept
+ {
+ return (decltype(__t)&&)__t;
+ }
+};
-template <std::size_t _Np, class... _Ts>
-constexpr decltype(auto) __nth_pack_element(_Ts&&... __ts) noexcept
+template <std::size_t _Np>
+struct __nth_pack_element_t
{
- return [&]<std::size_t... _Is>(
- std::index_sequence<_Is...>*) noexcept -> decltype(auto) {
- return stdexec::__nth_pack_element_<_Is...>((_Ts&&)__ts...);
- }((std::make_index_sequence<_Np>*)nullptr);
-}
+ template <std::size_t... _Is>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ static constexpr auto __impl(__indices<_Is...>) noexcept
+ {
+ return __nth_pack_element_impl<__ignore_t<_Is>...>();
+ }
-template <class _Ty>
+ template <class... _Ts>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr decltype(auto) operator()(_Ts&&... __ts) const noexcept
+ {
+ static_assert(_Np < sizeof...(_Ts));
+ return __impl(__make_indices<_Np>())((_Ts&&)__ts...);
+ }
+};
+
+template <std::size_t _Np>
+inline constexpr __nth_pack_element_t<_Np> __nth_pack_element{};
+
+template <auto... _Vs>
+struct __mliterals
+{
+ template <std::size_t _Np>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ static constexpr auto __nth() noexcept
+ {
+ return stdexec::__nth_pack_element<_Np>(_Vs...);
+ }
+};
+
+template <std::size_t _Np>
+struct __nth_member
+{
+ template <class _Ty>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr decltype(auto) operator()(_Ty && __ty) const noexcept
+ {
+ return ((_Ty&&)__ty).*(__ty.__mbrs_.template __nth<_Np>());
+ }
+};
+
+template <class _Ty, std::size_t _Offset = 0>
struct __mdispatch_
{
template <class... _Ts>
@@ -967,60 +1138,131 @@
}
};
-template <std::size_t _Np>
-struct __mdispatch_<__placeholder<_Np>>
+template <std::size_t _Np, std::size_t _Offset>
+struct __mdispatch_<__placeholder<_Np>, _Offset>
{
template <class... _Ts>
decltype(auto) operator()(_Ts&&... __ts) const noexcept
{
- return stdexec::__nth_pack_element<_Np>((_Ts&&)__ts...);
+ return stdexec::__nth_pack_element<_Np + _Offset>((_Ts&&)__ts...);
}
};
-template <std::size_t _Np>
-struct __mdispatch_<__placeholder<_Np>&>
+template <std::size_t _Np, std::size_t _Offset>
+struct __mdispatch_<__placeholder<_Np>&, _Offset>
{
template <class... _Ts>
decltype(auto) operator()(_Ts&&... __ts) const noexcept
{
- return stdexec::__nth_pack_element<_Np>(__ts...);
+ return stdexec::__nth_pack_element<_Np + _Offset>(__ts...);
}
};
-template <std::size_t _Np>
-struct __mdispatch_<__placeholder<_Np>&&>
+template <std::size_t _Np, std::size_t _Offset>
+struct __mdispatch_<__placeholder<_Np>&&, _Offset>
{
template <class... _Ts>
decltype(auto) operator()(_Ts&&... __ts) const noexcept
{
- return std::move(stdexec::__nth_pack_element<_Np>(__ts...));
+ return std::move(stdexec::__nth_pack_element<_Np + _Offset>(__ts...));
}
};
-template <std::size_t _Np>
-struct __mdispatch_<const __placeholder<_Np>&>
+template <std::size_t _Np, std::size_t _Offset>
+struct __mdispatch_<const __placeholder<_Np>&, _Offset>
{
template <class... _Ts>
decltype(auto) operator()(_Ts&&... __ts) const noexcept
{
- return std::as_const(stdexec::__nth_pack_element<_Np>(__ts...));
+ return std::as_const(
+ stdexec::__nth_pack_element<_Np + _Offset>(__ts...));
}
};
-template <class _Ret, class... _Args>
-struct __mdispatch_<_Ret (*)(_Args...)>
+template <class _Ret, class... _Args, std::size_t _Offset>
+struct __mdispatch_<_Ret (*)(_Args...), _Offset>
{
template <class... _Ts>
- requires(__callable<__mdispatch_<_Args>, _Ts...> && ...) &&
- __callable<_Ret,
- __call_result_t<__mdispatch_<_Args>, _Ts...>...>
+ requires(__callable<__mdispatch_<_Args, _Offset>, _Ts...> && ...) &&
+ __callable<_Ret, __call_result_t<__mdispatch_<_Args, _Offset>,
+ _Ts...>...>
+ auto operator()(_Ts&&... __ts) const noexcept(
+ __nothrow_callable<
+ _Ret, __call_result_t<__mdispatch_<_Args, _Offset>, _Ts...>...>)
+ -> __call_result_t<
+ _Ret, __call_result_t<__mdispatch_<_Args, _Offset>, _Ts...>...>
+ {
+ return _Ret{}(__mdispatch_<_Args, _Offset>{}((_Ts&&)__ts...)...);
+ }
+};
+
+template <class _Ret, class... _Args, std::size_t _Offset>
+struct __mdispatch_<_Ret (*)(_Args..., ...), _Offset>
+{
+ static_assert(_Offset == 0, "nested pack expressions are not supported");
+ using _Pattern = __mback<_Args...>;
+ static constexpr std::size_t __offset =
+ __get_placeholder_offset((__mtype<_Pattern>*)nullptr);
+
+ struct __impl
+ {
+ template <std::size_t... _Idx, class... _Ts>
+ requires(__callable<__mdispatch_<_Args>, _Ts...> && ...) &&
+ (__callable<__mdispatch_<_Pattern, _Idx + 1>, _Ts...> &&
+ ...) &&
+ __callable< //
+ _Ret, __call_result_t<__mdispatch_<_Args>, _Ts...>...,
+ __call_result_t<__mdispatch_<_Pattern, _Idx + 1>,
+ _Ts...>...>
+ auto operator()(__indices<_Idx...>, _Ts&&... __ts) const noexcept(
+ __nothrow_callable< //
+ _Ret, //
+ __call_result_t<__mdispatch_<_Args>, _Ts...>..., //
+ __call_result_t<__mdispatch_<_Pattern, _Idx + 1>, _Ts...>...>)
+ -> __call_result_t< //
+ _Ret, __call_result_t<__mdispatch_<_Args>, _Ts...>...,
+ __call_result_t<__mdispatch_<_Pattern, _Idx + 1>, _Ts...>...>
+ {
+ return _Ret()( //
+ __mdispatch_<_Args>()((_Ts&&)__ts...)..., //
+ __mdispatch_<_Pattern, _Idx + 1>()((_Ts&&)__ts...)...);
+ }
+ };
+
+ template <class... _Ts>
+ requires(__offset < sizeof...(_Ts)) &&
+ __callable<__impl,
+ __make_indices<sizeof...(_Ts) - __offset - 1>,
+ _Ts...>
+ auto operator()(_Ts&&... __ts) const noexcept(
+ __nothrow_callable<
+ __impl, __make_indices<sizeof...(_Ts) - __offset - 1>, _Ts...>)
+ -> __msecond<
+ __if_c<(__offset < sizeof...(_Ts))>,
+ __call_result_t<
+ __impl, __make_indices<sizeof...(_Ts) - __offset - 1>, _Ts...>>
+ {
+ return __impl()(__make_indices<sizeof...(_Ts) - __offset - 1>(),
+ (_Ts&&)__ts...);
+ }
+
+ template <class... _Ts>
+ requires(sizeof...(_Ts) == __offset) &&
+ __callable<
+ __mdispatch_<__minvoke<__mpop_back<__qf<_Ret>>, _Args...>*>,
+ _Ts...>
auto operator()(_Ts&&... __ts) const
noexcept(__nothrow_callable<
- _Ret, __call_result_t<__mdispatch_<_Args>, _Ts...>...>)
- -> __call_result_t<_Ret,
- __call_result_t<__mdispatch_<_Args>, _Ts...>...>
+ __mdispatch_<__minvoke<__mpop_back<__qf<_Ret>>, _Args...>*>,
+ _Ts...>)
+ -> __msecond<
+ __if_c<(sizeof...(_Ts) == __offset)>,
+ __call_result_t<
+ __mdispatch_<__minvoke<__mpop_back<__qf<_Ret>>, _Args...>*>,
+ _Ts...>>
{
- return _Ret{}(__mdispatch_<_Args>{}((_Ts&&)__ts...)...);
+ return __mdispatch_<__minvoke<__mpop_back<__qf<_Ret>>, _Args...>*>()(
+ (_Ts&&)__ts...);
}
};
@@ -1029,21 +1271,13 @@
{};
template <class _Ret, class... _Args>
-struct __mdispatch<_Ret(_Args...)>
-{
- template <class... _Ts>
- requires(__callable<__mdispatch_<_Args>, _Ts...> && ...) &&
- __callable<_Ret,
- __call_result_t<__mdispatch_<_Args>, _Ts...>...>
- auto operator()(_Ts&&... __ts) const
- noexcept(__nothrow_callable<
- _Ret, __call_result_t<__mdispatch_<_Args>, _Ts...>...>)
- -> __call_result_t<_Ret,
- __call_result_t<__mdispatch_<_Args>, _Ts...>...>
- {
- return _Ret{}(__mdispatch_<_Args>{}((_Ts&&)__ts...)...);
- }
-};
+struct __mdispatch<_Ret(_Args...)> : __mdispatch_<_Ret (*)(_Args...)>
+{};
+
+template <class _Ret, class... _Args>
+struct __mdispatch<_Ret(_Args..., ...)> : __mdispatch_<_Ret (*)(_Args..., ...)>
+{};
+
template <class _Ty, class... _Ts>
concept __dispatchable = __callable<__mdispatch<_Ty>, _Ts...>;
@@ -1073,8 +1307,7 @@
template <class _Signatures, class _DefaultFn, class... _Args>
using __make_dispatcher = //
- __minvoke<
- __if_c<__minvocable<__which<_Signatures>, _Args...>,
- __mcompose<__q<__mdispatch>, __which<_Signatures>>, _DefaultFn>,
- _Args...>;
+ __minvoke<__mtry_catch<__mcompose<__q<__mdispatch>, __which<_Signatures>>,
+ _DefaultFn>,
+ _Args...>;
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/__detail/__p2300.hpp b/include/sdbusplus/async/stdexec/__detail/__p2300.hpp
index 56057ba..e7a2c18 100644
--- a/include/sdbusplus/async/stdexec/__detail/__p2300.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__p2300.hpp
@@ -100,7 +100,10 @@
stdexec::stop_token_of_t<_StopTokenProvider>;
// [exec.env], execution environments
-using no_env STDEXEC_STD_DEPRECATED = stdexec::no_env;
+struct __no_env
+{};
+
+using no_env STDEXEC_STD_DEPRECATED = __no_env;
using get_env_t STDEXEC_STD_DEPRECATED = stdexec::get_env_t;
// using forwarding_env_query_t STDEXEC_STD_DEPRECATED =
// stdexec::forwarding_env_query_t; // BUGBUG
@@ -155,14 +158,14 @@
inline constexpr stdexec::start_t start{};
// [exec.snd], senders
-template <class _Sender, class _Env = stdexec::no_env>
+template <class _Sender, class _Env = __no_env>
concept sender /*STDEXEC_STD_DEPRECATED*/ = stdexec::sender_in<_Sender, _Env>;
template <class _Sender, class _Receiver>
concept sender_to /*STDEXEC_STD_DEPRECATED*/ =
stdexec::sender_to<_Sender, _Receiver>;
-template <class _Sender, class _SetSig, class _Env = stdexec::no_env>
+template <class _Sender, class _SetSig, class _Env = __no_env>
concept sender_of /*STDEXEC_STD_DEPRECATED*/ =
stdexec::sender_of<_Sender, _SetSig, _Env>;
@@ -173,30 +176,34 @@
inline constexpr stdexec::get_completion_signatures_t
get_completion_signatures{};
-template <class _Sender, class _Env = stdexec::no_env>
+template <class _Sender, class _Env = __no_env>
using completion_signatures_of_t STDEXEC_STD_DEPRECATED =
stdexec::completion_signatures_of_t<_Sender, _Env>;
template <class _Env>
+struct __dependent_completion_signatures
+{};
+
+template <class _Env>
using dependent_completion_signatures STDEXEC_STD_DEPRECATED =
- stdexec::dependent_completion_signatures<_Env>;
+ __dependent_completion_signatures<_Env>;
template < //
class _Sender, //
- class _Env = stdexec::no_env, //
+ class _Env = __no_env, //
template <class...> class _Tuple = stdexec::__decayed_tuple, //
template <class...> class _Variant = stdexec::__variant>
using value_types_of_t STDEXEC_STD_DEPRECATED =
stdexec::value_types_of_t<_Sender, _Env, _Tuple, _Variant>;
-template < //
- class _Sender, //
- class _Env = stdexec::no_env, //
+template < //
+ class _Sender, //
+ class _Env = __no_env, //
template <class...> class _Variant = stdexec::__variant>
using error_types_of_t STDEXEC_STD_DEPRECATED =
stdexec::error_types_of_t<_Sender, _Env, _Variant>;
-template <class _Sender, class _Env = stdexec::no_env>
+template <class _Sender, class _Env = __no_env>
STDEXEC_STD_DEPRECATED inline constexpr bool sends_stopped =
stdexec::sends_stopped<_Sender, _Env>;
@@ -325,7 +332,7 @@
// [exec.utils.mkcmplsigs]
template < //
class _Sender, //
- class _Env = stdexec::no_env,
+ class _Env = __no_env,
class _Sigs = stdexec::completion_signatures<>, //
template <class...>
class _SetValue = stdexec::__compl_sigs::__default_set_value, //
diff --git a/include/sdbusplus/async/stdexec/__detail/__ranges.hpp b/include/sdbusplus/async/stdexec/__detail/__ranges.hpp
new file mode 100644
index 0000000..f18f84a
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/__detail/__ranges.hpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2023 NVIDIA Corporation
+ *
+ * Licensed under the Apache License Version 2.0 with LLVM Exceptions
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://llvm.org/LICENSE.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "__config.hpp"
+#include "__type_traits.hpp"
+
+#if 0 // STDEXEC_HAS_STD_RANGES()
+
+#include <ranges>
+
+namespace stdexec::ranges {
+ using std::ranges::begin;
+ using std::ranges::end;
+
+ using std::ranges::range_value_t;
+ using std::ranges::range_reference_t;
+ using std::ranges::iterator_t;
+ using std::ranges::sentinel_t;
+}
+
+#else
+
+#include <iterator>
+
+namespace stdexec::ranges
+{
+
+namespace __detail
+{
+void begin();
+void end();
+
+template <class _Ty>
+concept __has_member_begin = requires(_Ty&& __v) { ((_Ty&&)__v).begin(); };
+
+template <class _Ty>
+concept __has_free_begin = __has_member_begin<_Ty> ||
+ requires(_Ty&& __v) { begin(((_Ty&&)__v)); };
+
+template <class _Ty>
+concept __has_member_end = requires(_Ty&& __v) { ((_Ty&&)__v).end(); };
+
+template <class _Ty>
+concept __has_free_end = __has_member_end<_Ty> ||
+ requires(_Ty&& __v) { end(((_Ty&&)__v)); };
+
+struct __begin_t
+{
+ template <class _Range>
+ requires __has_member_begin<_Range>
+ auto operator()(_Range&& __rng) const
+ noexcept(noexcept(((_Range&&)__rng).begin()))
+ -> decltype(((_Range&&)__rng).begin())
+ {
+ return ((_Range&&)__rng).begin();
+ }
+
+ template <class _Range>
+ requires __has_free_begin<_Range>
+ auto operator()(_Range&& __rng) const
+ noexcept(noexcept(begin(((_Range&&)__rng))))
+ -> decltype(begin(((_Range&&)__rng)))
+ {
+ return begin(((_Range&&)__rng));
+ }
+};
+
+struct __end_t
+{
+ template <class _Range>
+ requires __has_member_end<_Range>
+ auto operator()(_Range&& __rng) const
+ noexcept(noexcept(((_Range&&)__rng).end()))
+ -> decltype(((_Range&&)__rng).end())
+ {
+ return ((_Range&&)__rng).end();
+ }
+
+ template <class _Range>
+ requires __has_free_end<_Range>
+ auto operator()(_Range&& __rng) const
+ noexcept(noexcept(end(((_Range&&)__rng))))
+ -> decltype(end(((_Range&&)__rng)))
+ {
+ return end(((_Range&&)__rng));
+ }
+};
+} // namespace __detail
+
+inline constexpr __detail::__begin_t begin{};
+inline constexpr __detail::__end_t end{};
+
+template <class _Range>
+using iterator_t = decltype(begin((__declval<_Range>())));
+
+template <class _Range>
+using sentinel_t = decltype(end((__declval<_Range>())));
+
+template <class _Range>
+using range_reference_t = decltype(*begin((__declval<_Range>())));
+
+template <class _Range>
+using range_value_t =
+ typename std::iterator_traits<iterator_t<_Range>>::value_type;
+
+} // namespace stdexec::ranges
+
+#endif
diff --git a/include/sdbusplus/async/stdexec/__detail/__scope.hpp b/include/sdbusplus/async/stdexec/__detail/__scope.hpp
index 5ad6b93..ee1847d 100644
--- a/include/sdbusplus/async/stdexec/__detail/__scope.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__scope.hpp
@@ -26,8 +26,8 @@
template <class _Fn>
struct __scope_guard<_Fn>
{
- STDEXEC_NO_UNIQUE_ADDRESS _Fn __fn_;
- STDEXEC_NO_UNIQUE_ADDRESS __immovable __hidden_{};
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fn __fn_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) __immovable __hidden_{};
bool __dismissed_{false};
~__scope_guard()
@@ -45,9 +45,9 @@
template <class _Fn, class _T0>
struct __scope_guard<_Fn, _T0>
{
- STDEXEC_NO_UNIQUE_ADDRESS _Fn __fn_;
- STDEXEC_NO_UNIQUE_ADDRESS _T0 __t0_;
- STDEXEC_NO_UNIQUE_ADDRESS __immovable __hidden_{};
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fn __fn_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _T0 __t0_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) __immovable __hidden_{};
bool __dismissed_{false};
@@ -66,10 +66,10 @@
template <class _Fn, class _T0, class _T1>
struct __scope_guard<_Fn, _T0, _T1>
{
- STDEXEC_NO_UNIQUE_ADDRESS _Fn __fn_;
- STDEXEC_NO_UNIQUE_ADDRESS _T0 __t0_;
- STDEXEC_NO_UNIQUE_ADDRESS _T1 __t1_;
- STDEXEC_NO_UNIQUE_ADDRESS __immovable __hidden_{};
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fn __fn_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _T0 __t0_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _T1 __t1_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) __immovable __hidden_{};
bool __dismissed_{false};
@@ -88,11 +88,11 @@
template <class _Fn, class _T0, class _T1, class _T2>
struct __scope_guard<_Fn, _T0, _T1, _T2>
{
- STDEXEC_NO_UNIQUE_ADDRESS _Fn __fn_;
- STDEXEC_NO_UNIQUE_ADDRESS _T0 __t0_;
- STDEXEC_NO_UNIQUE_ADDRESS _T1 __t1_;
- STDEXEC_NO_UNIQUE_ADDRESS _T2 __t2_;
- STDEXEC_NO_UNIQUE_ADDRESS __immovable __hidden_{};
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fn __fn_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _T0 __t0_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _T1 __t1_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _T2 __t2_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) __immovable __hidden_{};
bool __dismissed_{false};
diff --git a/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp b/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp
index 93e303a..2ec69d6 100644
--- a/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp
+++ b/include/sdbusplus/async/stdexec/__detail/__type_traits.hpp
@@ -179,4 +179,11 @@
template <class _From, class _To>
using __copy_cvref_t = typename __copy_cvref_fn<_From>::template __f<_To>;
+#if !STDEXEC_HAS_BUILTIN(__is_const)
+template <class>
+inline constexpr bool __is_const = false;
+template <class _Up>
+inline constexpr bool __is_const<const _Up> = true;
+#endif
+
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/any_sender_of.hpp b/include/sdbusplus/async/stdexec/any_sender_of.hpp
index 391cd86..443b64c 100644
--- a/include/sdbusplus/async/stdexec/any_sender_of.hpp
+++ b/include/sdbusplus/async/stdexec/any_sender_of.hpp
@@ -15,7 +15,9 @@
*/
#pragma once
-#include <sdbusplus/async/stdexec/execution.hpp>
+#include "../stdexec/execution.hpp"
+
+#include <sdbusplus/async/stdexec/sequence_senders.hpp>
#include <cstddef>
@@ -33,8 +35,8 @@
constexpr const _VTable* operator()(__mtype<_VTable>,
__mtype<_Tp>) const noexcept
{
- return tag_invoke(__create_vtable_t{}, __mtype<_VTable>{},
- __mtype<_Tp>{});
+ return stdexec::tag_invoke(__create_vtable_t{}, __mtype<_VTable>{},
+ __mtype<_Tp>{});
}
};
@@ -207,7 +209,7 @@
{
static_assert(
nothrow_tag_invocable<__delete_t, __mtype<_Tp>, _Storage&>);
- tag_invoke(__delete_t{}, __mtype<_Tp>{}, __storage);
+ stdexec::tag_invoke(__delete_t{}, __mtype<_Tp>{}, __storage);
}
};
@@ -225,7 +227,8 @@
__mtype<_Tp>, _Storage&,
const _Storage&>)
{
- tag_invoke(__copy_construct_t{}, __mtype<_Tp>{}, __self, __from);
+ stdexec::tag_invoke(__copy_construct_t{}, __mtype<_Tp>{}, __self,
+ __from);
}
};
@@ -241,8 +244,8 @@
{
static_assert(nothrow_tag_invocable<__move_construct_t, __mtype<_Tp>,
_Storage&, _Storage&&>);
- tag_invoke(__move_construct_t{}, __mtype<_Tp>{}, __self,
- (_Storage&&)__from);
+ stdexec::tag_invoke(__move_construct_t{}, __mtype<_Tp>{}, __self,
+ (_Storage&&)__from);
}
};
@@ -441,7 +444,7 @@
__default_storage_vtable((__vtable_t*)nullptr)};
void* __object_pointer_{nullptr};
alignas(__alignment) std::byte __buffer_[__buffer_size]{};
- STDEXEC_NO_UNIQUE_ADDRESS _Allocator __allocator_{};
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Allocator __allocator_{};
};
};
@@ -675,7 +678,7 @@
const __vtable_t* __vtable_{__default_storage_vtable((__vtable_t*)nullptr)};
void* __object_pointer_{nullptr};
alignas(__alignment) std::byte __buffer_[__buffer_size]{};
- STDEXEC_NO_UNIQUE_ADDRESS _Allocator __allocator_{};
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Allocator __allocator_{};
};
struct __empty_vtable
@@ -779,7 +782,12 @@
requires(__is_not_stop_token_query<_Queries> && ...)
struct __ref<completion_signatures<_Sigs...>, _Queries...>
{
+#if !STDEXEC_MSVC()
+ // MSVCBUG
+ // https://developercommunity.visualstudio.com/t/Private-member-inaccessible-when-used-in/10448363
+
private:
+#endif
using __vtable_t =
stdexec::__t<__vtable<completion_signatures<_Sigs...>, _Queries...>>;
@@ -851,7 +859,12 @@
requires(__is_stop_token_query<_Queries> || ...)
struct __ref<completion_signatures<_Sigs...>, _Queries...>
{
+#if !STDEXEC_MSVC()
+ // MSVCBUG
+ // https://developercommunity.visualstudio.com/t/Private-member-inaccessible-when-used-in/10448363
+
private:
+#endif
using _FilteredQueries =
__minvoke<__remove_if<__q<__is_never_stop_token_query>>, _Queries...>;
using __vtable_t = stdexec::__t<
@@ -944,7 +957,7 @@
template <class _Receiver>
struct __operation_base
{
- STDEXEC_NO_UNIQUE_ADDRESS _Receiver __rcvr_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_;
stdexec::in_place_stop_source __stop_source_{};
using __stop_callback = typename stdexec::stop_token_of_t<
stdexec::env_of_t<_Receiver>>::template callback_type<__on_stop_t>;
@@ -962,8 +975,17 @@
struct __t
{
+ using is_receiver = void;
__operation_base<_Receiver>* __op_;
+ template <same_as<set_next_t> _SetNext, same_as<__t> _Self, class _Item>
+ requires __callable<_SetNext, _Receiver&, _Item>
+ friend auto tag_invoke(_SetNext, _Self& __self, _Item&& __item)
+ {
+ return _SetNext{}(__self.__op_->__rcvr_,
+ static_cast<_Item&&>(__item));
+ }
+
template <same_as<set_value_t> _SetValue, same_as<__t> _Self,
class... _Args>
requires __callable<_SetValue, _Receiver&&, _Args...>
@@ -998,10 +1020,9 @@
friend __env_t<env_of_t<_Receiver>>
tag_invoke(_GetEnv, const _Self& __self) noexcept
{
- return __make_env(
- get_env(__self.__op_->__rcvr_),
- __with_(get_stop_token,
- __self.__op_->__stop_source_.get_token()));
+ return __make_env(get_env(__self.__op_->__rcvr_),
+ __mkprop(__self.__op_->__stop_source_.get_token(),
+ get_stop_token));
}
};
};
@@ -1058,7 +1079,7 @@
{}
private:
- STDEXEC_NO_UNIQUE_ADDRESS _Receiver __rec_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rec_;
__immovable_operation_storage __storage_{};
friend void tag_invoke(start_t, __t& __self) noexcept
@@ -1330,7 +1351,8 @@
std::is_nothrow_invocable_v<
_Tag, stdexec::__copy_cvref_t<Self, __receiver_base>, _As...>)
{
- return tag_invoke(_Tag{}, ((Self&&)__self).__receiver_, (_As&&)__as...);
+ return stdexec::tag_invoke(_Tag{}, ((Self&&)__self).__receiver_,
+ (_As&&)__as...);
}
public:
@@ -1363,8 +1385,8 @@
std::is_nothrow_invocable_v<
_Tag, stdexec::__copy_cvref_t<Self, __sender_base>, _As...>)
{
- return tag_invoke(_Tag{}, ((Self&&)__self).__sender_,
- (_As&&)__as...);
+ return stdexec::tag_invoke(_Tag{}, ((Self&&)__self).__sender_,
+ (_As&&)__as...);
}
public:
@@ -1405,9 +1427,21 @@
stdexec::get_completion_scheduler_t<stdexec::set_value_t>>>,
decltype(_SenderQueries)...>;
+#if STDEXEC_MSVC()
+ // MSVCBUG
+ // https://developercommunity.visualstudio.com/t/ICE-and-non-ICE-bug-in-NTTP-argument-w/10361081
+
+ static constexpr auto __any_scheduler_noexcept_signature =
+ stdexec::get_completion_scheduler<stdexec::set_value_t>.signature<any_scheduler() noexcept>;
+ template <class... _Queries>
+ using __schedule_sender_fn =
+ typename __schedule_receiver::template any_sender<
+ __any_scheduler_noexcept_signature>;
+#else
template <class... _Queries>
using __schedule_sender_fn = typename __schedule_receiver::template any_sender<
stdexec::get_completion_scheduler<stdexec::set_value_t>.template signature<any_scheduler() noexcept>>;
+#endif
using __schedule_sender =
stdexec::__mapply<stdexec::__q<__schedule_sender_fn>,
schedule_sender_queries>;
@@ -1440,8 +1474,8 @@
_Tag, stdexec::__copy_cvref_t<Self, __scheduler_base>,
_As...>)
{
- return tag_invoke(_Tag{}, ((Self&&)__self).__scheduler_,
- (_As&&)__as...);
+ return stdexec::tag_invoke(
+ _Tag{}, ((Self&&)__self).__scheduler_, (_As&&)__as...);
}
friend bool
diff --git a/include/sdbusplus/async/stdexec/async_scope.hpp b/include/sdbusplus/async/stdexec/async_scope.hpp
index d806fc1..3da1840 100644
--- a/include/sdbusplus/async/stdexec/async_scope.hpp
+++ b/include/sdbusplus/async/stdexec/async_scope.hpp
@@ -62,7 +62,7 @@
struct __when_empty_op_base : __task
{
using _Receiver = __t<_ReceiverId>;
- STDEXEC_NO_UNIQUE_ADDRESS _Receiver __rcvr_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_;
};
template <class _ConstrainedId, class _ReceiverId>
@@ -129,7 +129,10 @@
template <__decays_to<__when_empty_sender> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> completion_signatures_of_t<__copy_cvref_t<_Self, _Constrained>,
- __env_t<_Env>>;
+ __env_t<_Env>>
+ {
+ return {};
+ }
friend empty_env tag_invoke(get_env_t, const __when_empty_sender&) noexcept
{
@@ -137,7 +140,7 @@
}
const __impl* __scope_;
- STDEXEC_NO_UNIQUE_ADDRESS _Constrained __c_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Constrained __c_;
};
template <class _Constrained>
@@ -150,7 +153,7 @@
{
using _Receiver = __t<_ReceiverId>;
const __impl* __scope_;
- STDEXEC_NO_UNIQUE_ADDRESS _Receiver __rcvr_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_;
};
template <class _ReceiverId>
@@ -238,7 +241,7 @@
using is_sender = void;
const __impl* __scope_;
- STDEXEC_NO_UNIQUE_ADDRESS _Constrained __c_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Constrained __c_;
template <class _Receiver>
using __nest_operation_t = __nest_op<_ConstrainedId, __x<_Receiver>>;
@@ -254,10 +257,14 @@
return __nest_operation_t<_Receiver>{
__self.__scope_, ((_Self&&)__self).__c_, (_Receiver&&)__rcvr};
}
+
template <__decays_to<__nest_sender> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> completion_signatures_of_t<__copy_cvref_t<_Self, _Constrained>,
- __env_t<_Env>>;
+ __env_t<_Env>>
+ {
+ return {};
+ }
friend empty_env tag_invoke(get_env_t, const __nest_sender&) noexcept
{
@@ -393,9 +400,10 @@
}
}
- STDEXEC_NO_UNIQUE_ADDRESS _Receiver __rcvr_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_;
std::unique_ptr<__future_state<_Sender, _Env>> __state_;
- STDEXEC_NO_UNIQUE_ADDRESS __forward_consumer __forward_consumer_;
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ __forward_consumer __forward_consumer_;
public:
~__future_op() noexcept
@@ -683,7 +691,10 @@
template <__decays_to<__future> _Self, class _OtherEnv>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _OtherEnv&&)
- -> __completions_t<_Self>;
+ -> __completions_t<_Self>
+ {
+ return {};
+ }
friend empty_env tag_invoke(get_env_t, const __future&) noexcept
{
@@ -699,11 +710,17 @@
////////////////////////////////////////////////////////////////////////////
// async_scope::spawn implementation
+template <class _Env>
+using __spawn_env_t =
+ __result_of<__join_env, _Env,
+ __env::__prop<in_place_stop_token(get_stop_token_t)>,
+ __env::__prop<__inln::__scheduler(get_scheduler_t)>>;
+
template <class _EnvId>
struct __spawn_op_base
{
using _Env = __t<_EnvId>;
- __env_t<_Env> __env_;
+ __spawn_env_t<_Env> __env_;
void (*__delete_)(__spawn_op_base*);
};
@@ -729,8 +746,8 @@
std::terminate();
}
- friend const __env_t<_Env>& tag_invoke(get_env_t,
- const __spawn_rcvr& __self) noexcept
+ friend const __spawn_env_t<_Env>&
+ tag_invoke(get_env_t, const __spawn_rcvr& __self) noexcept
{
return __self.__op_->__env_;
}
@@ -748,8 +765,10 @@
template <__decays_to<_Sender> _Sndr>
__spawn_op(_Sndr&& __sndr, _Env __env, const __impl* __scope) :
__spawn_op_base<_EnvId>{
- make_env((_Env&&)__env,
- with(get_stop_token, __scope->__stop_source_.get_token())),
+ __join_env(
+ (_Env&&)__env,
+ __mkprop(__scope->__stop_source_.get_token(), get_stop_token),
+ __mkprop(__inln::__scheduler{}, get_scheduler)),
[](__spawn_op_base<_EnvId>* __op) {
delete static_cast<__spawn_op*>(__op);
}},
@@ -802,7 +821,7 @@
}
template <__movable_value _Env = empty_env,
- sender_in<__env_t<_Env>> _Sender>
+ sender_in<__spawn_env_t<_Env>> _Sender>
requires sender_to<nest_result_t<_Sender>, __spawn_receiver_t<_Env>>
void spawn(_Sender&& __sndr, _Env __env = {})
{
diff --git a/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp b/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp
index dc6df2e..e0e6235 100644
--- a/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp
+++ b/include/sdbusplus/async/stdexec/at_coroutine_exit.hpp
@@ -100,11 +100,10 @@
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> dependent_completion_signatures<_Env>;
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> __completion_signatures<_Env>
- requires true;
+ {
+ return {};
+ }
friend env_of_t<_Sender> tag_invoke(get_env_t,
const __t& __self) noexcept
@@ -194,8 +193,7 @@
auto __coro = __p.__is_unhandled_stopped_
? __p.continuation().unhandled_stopped()
: __p.continuation().handle();
- __h.destroy();
- return __coro;
+ return STDEXEC_DESTROY_AND_CONTINUE(__h, __coro);
}
void await_resume() const noexcept {}
diff --git a/include/sdbusplus/async/stdexec/commit.info b/include/sdbusplus/async/stdexec/commit.info
index 6c3c5cd..8556544 100644
--- a/include/sdbusplus/async/stdexec/commit.info
+++ b/include/sdbusplus/async/stdexec/commit.info
@@ -1 +1 @@
-f1409100e46d3bdf2cbf2d6fb6c8e4a96e8bd307
+6b027bc6b6ace7b9047656cf8eb6f934e0535da3
diff --git a/include/sdbusplus/async/stdexec/concepts.hpp b/include/sdbusplus/async/stdexec/concepts.hpp
index 3098123..a5ebe4a 100644
--- a/include/sdbusplus/async/stdexec/concepts.hpp
+++ b/include/sdbusplus/async/stdexec/concepts.hpp
@@ -101,7 +101,7 @@
template <class T>
concept destructible = __destructible_<T>;
-#if __has_builtin(__is_constructible)
+#if STDEXEC_HAS_BUILTIN(__is_constructible)
template <class _Ty, class... _As>
concept constructible_from = //
destructible<_Ty> && //
@@ -110,7 +110,7 @@
template <class _Ty, class... _As>
concept constructible_from = //
destructible<_Ty> && //
- is_constructible_v<_Ty, _As...>;
+ std::is_constructible_v<_Ty, _As...>;
#endif
template <class _Ty>
@@ -232,7 +232,16 @@
move_constructible<__decay_t<_Ty>> && //
constructible_from<__decay_t<_Ty>, _Ty>;
-#if __has_builtin(__is_nothrow_constructible)
+template <class _Ty>
+concept __nothrow_movable_value = //
+ __movable_value<_Ty> && //
+ requires(_Ty&& __t) {
+ {
+ __decay_t<_Ty>{__decay_t<_Ty>{(_Ty&&)__t}}
+ } noexcept;
+ };
+
+#if STDEXEC_HAS_BUILTIN(__is_nothrow_constructible)
template <class _Ty, class... _As>
concept __nothrow_constructible_from = constructible_from<_Ty, _As...> &&
__is_nothrow_constructible(_Ty, _As...);
@@ -257,8 +266,8 @@
concept __nothrow_decay_copyable =
__nothrow_constructible_from<__decay_t<_Ty>, _Ty>;
-template <class _Range>
-using range_value_t = decltype(*begin(::std::declval<_Range>()));
+template <class _Ty, class _Up>
+concept __decays_to_derived_from = derived_from<__decay_t<_Ty>, _Up>;
} // namespace stdexec
diff --git a/include/sdbusplus/async/stdexec/coroutine.hpp b/include/sdbusplus/async/stdexec/coroutine.hpp
index 3c5c4c8..3391043 100644
--- a/include/sdbusplus/async/stdexec/coroutine.hpp
+++ b/include/sdbusplus/async/stdexec/coroutine.hpp
@@ -40,70 +40,89 @@
template <class _Awaiter, class _Promise>
concept __with_await_suspend =
same_as<_Promise, void> || //
- requires(_Awaiter& __await, __coro::coroutine_handle<_Promise> __h) {
+ requires(_Awaiter& __awaiter, __coro::coroutine_handle<_Promise> __h) {
{
- __await.await_suspend(__h)
+ __awaiter.await_suspend(__h)
} -> __await_suspend_result;
};
template <class _Awaiter, class _Promise = void>
concept __awaiter = //
- requires(_Awaiter& __await) {
- __await.await_ready() ? 1 : 0;
- __await.await_resume();
+ requires(_Awaiter& __awaiter) {
+ __awaiter.await_ready() ? 1 : 0;
+ __awaiter.await_resume();
} && //
__with_await_suspend<_Awaiter, _Promise>;
+#if STDEXEC_MSVC()
+// MSVCBUG
+// https://developercommunity.visualstudio.com/t/operator-co_await-not-found-in-requires/10452721
+
template <class _Awaitable>
-decltype(auto) __get_awaiter(_Awaitable&& __await, void*)
+void __co_await_constraint(_Awaitable&& __awaitable)
+ requires requires { operator co_await((_Awaitable&&)__awaitable); };
+#endif
+
+template <class _Awaitable>
+decltype(auto) __get_awaiter(_Awaitable&& __awaitable, void*)
{
- if constexpr (requires { ((_Awaitable&&)__await).operator co_await(); })
+ if constexpr (requires { ((_Awaitable&&)__awaitable).operator co_await(); })
{
- return ((_Awaitable&&)__await).operator co_await();
+ return ((_Awaitable&&)__awaitable).operator co_await();
}
- else if constexpr (requires { operator co_await((_Awaitable&&)__await); })
+ else if constexpr (requires {
+#if STDEXEC_MSVC()
+ __co_await_constraint((_Awaitable&&)__awaitable);
+#else
+ operator co_await((_Awaitable&&) __awaitable);
+#endif
+ })
{
- return operator co_await((_Awaitable&&)__await);
+ return operator co_await((_Awaitable&&)__awaitable);
}
else
{
- return (_Awaitable&&)__await;
+ return (_Awaitable&&)__awaitable;
}
}
template <class _Awaitable, class _Promise>
-decltype(auto) __get_awaiter(_Awaitable&& __await, _Promise* __promise)
- requires requires { __promise->await_transform((_Awaitable&&)__await); }
+decltype(auto) __get_awaiter(_Awaitable&& __awaitable, _Promise* __promise)
+ requires requires { __promise->await_transform((_Awaitable&&)__awaitable); }
{
if constexpr (requires {
- __promise->await_transform((_Awaitable&&)__await)
+ __promise->await_transform((_Awaitable&&)__awaitable)
.
operator co_await();
})
{
- return __promise->await_transform((_Awaitable&&)__await)
+ return __promise->await_transform((_Awaitable&&)__awaitable)
.
operator co_await();
}
else if constexpr (requires {
- operator co_await(__promise->await_transform(
- (_Awaitable&&)__await));
+#if STDEXEC_MSVC()
+ __co_await_constraint(__promise->await_transform(
+ (_Awaitable&&)__awaitable));
+#else
+ operator co_await(__promise->await_transform((_Awaitable&&) __awaitable));
+#endif
})
{
return operator co_await(
- __promise->await_transform((_Awaitable&&)__await));
+ __promise->await_transform((_Awaitable&&)__awaitable));
}
else
{
- return __promise->await_transform((_Awaitable&&)__await);
+ return __promise->await_transform((_Awaitable&&)__awaitable);
}
}
template <class _Awaitable, class _Promise = void>
concept __awaitable = //
- requires(_Awaitable&& __await, _Promise* __promise) {
+ requires(_Awaitable&& __awaitable, _Promise* __promise) {
{
- stdexec::__get_awaiter((_Awaitable&&)__await, __promise)
+ stdexec::__get_awaiter((_Awaitable&&)__awaitable, __promise)
} -> __awaiter<_Promise>;
};
diff --git a/include/sdbusplus/async/stdexec/env.hpp b/include/sdbusplus/async/stdexec/env.hpp
index 0beaf08..68fe431 100644
--- a/include/sdbusplus/async/stdexec/env.hpp
+++ b/include/sdbusplus/async/stdexec/env.hpp
@@ -17,30 +17,29 @@
#include "../stdexec/execution.hpp"
-#ifdef __EDG__
-#pragma diagnostic push
-#pragma diag_suppress 1302
-#endif
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_EDG(1302)
namespace exec
{
-template <class... _TagValue>
-using with_t = stdexec::__with<_TagValue...>;
+template <class _Tag, class _Value = void>
+using with_t = stdexec::__with<_Tag, _Value>;
namespace __detail
{
struct __with_t
{
template <class _Tag, class _Value>
- with_t<_Tag, _Value> operator()(_Tag, _Value&& __val) const
+ with_t<_Tag, stdexec::__decay_t<_Value>> operator()(_Tag,
+ _Value&& __val) const
{
- return stdexec::__with_(_Tag(), (_Value&&)__val);
+ return stdexec::__mkprop((_Value&&)__val, _Tag());
}
template <class _Tag>
with_t<_Tag> operator()(_Tag) const
{
- return stdexec::__with_(_Tag());
+ return stdexec::__mkprop(_Tag());
}
};
} // namespace __detail
@@ -64,7 +63,7 @@
using _Default = __t<_DefaultId>;
using _Receiver = __t<_ReceiverId>;
- STDEXEC_NO_UNIQUE_ADDRESS _Default __default_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Default __default_;
_Receiver __rcvr_;
friend void tag_invoke(start_t, __operation& __self) noexcept
@@ -94,7 +93,7 @@
{
using _Default = __t<_DefaultId>;
using is_sender = void;
- STDEXEC_NO_UNIQUE_ADDRESS _Default __default_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Default __default_;
template <class _Env>
using __value_t = __minvoke<
@@ -116,11 +115,12 @@
return {{}, ((_Self&&)__self).__default_, (_Receiver&&)__rcvr};
}
- friend auto tag_invoke(get_completion_signatures_t, __sender, no_env)
- -> dependent_completion_signatures<no_env>;
- template <__none_of<no_env> _Env>
+ template <class _Env>
friend auto tag_invoke(get_completion_signatures_t, __sender, _Env&&)
- -> __completions_t<_Env>;
+ -> __completions_t<_Env>
+ {
+ return {};
+ }
};
struct __read_with_default_t
@@ -136,137 +136,7 @@
inline constexpr __read_with_default::__read_with_default_t read_with_default{};
-namespace __write
-{
-using namespace stdexec;
-
-struct __write_t;
-
-template <class _ReceiverId, class _Env>
-struct __operation_base
-{
- using _Receiver = __t<_ReceiverId>;
- _Receiver __rcvr_;
- const _Env __env_;
-};
-
-template <class _ReceiverId, class _Env>
-struct __receiver
-{
- using _Receiver = stdexec::__t<_ReceiverId>;
-
- struct __t : receiver_adaptor<__t>
- {
- _Receiver&& base() && noexcept
- {
- return (_Receiver&&)__op_->__rcvr_;
- }
-
- const _Receiver& base() const& noexcept
- {
- return __op_->__rcvr_;
- }
-
- auto get_env() const noexcept
- -> __env::__env_join_t<const _Env&, env_of_t<_Receiver>>
- {
- return __env::__join_env(__op_->__env_, stdexec::get_env(base()));
- }
-
- __operation_base<_ReceiverId, _Env>* __op_;
- };
-};
-
-template <class _SenderId, class _ReceiverId, class _Env>
-struct __operation : __operation_base<_ReceiverId, _Env>
-{
- using _Sender = __t<_SenderId>;
- using __base_t = __operation_base<_ReceiverId, _Env>;
- using __receiver_t = __t<__receiver<_ReceiverId, _Env>>;
- connect_result_t<_Sender, __receiver_t> __state_;
-
- __operation(_Sender&& __sndr, auto&& __rcvr, auto&& __env) :
- __base_t{(decltype(__rcvr))__rcvr, (decltype(__env))__env},
- __state_{stdexec::connect((_Sender&&)__sndr, __receiver_t{{}, this})}
- {}
-
- friend void tag_invoke(start_t, __operation& __self) noexcept
- {
- start(__self.__state_);
- }
-};
-
-template <class _SenderId, class _Env>
-struct __sender
-{
- using _Sender = stdexec::__t<_SenderId>;
-
- template <class _Receiver>
- using __receiver_t = stdexec::__t<__receiver<__id<_Receiver>, _Env>>;
- template <class _Self, class _Receiver>
- using __operation_t = __operation<__id<__copy_cvref_t<_Self, _Sender>>,
- __id<_Receiver>, _Env>;
-
- struct __t
- {
- using is_sender = void;
- using __id = __sender;
- _Sender __sndr_;
- _Env __env_;
-
- template <__decays_to<__t> _Self, receiver _Receiver>
- requires sender_to<__copy_cvref_t<_Self, _Sender>,
- __receiver_t<_Receiver>>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr)
- -> __operation_t<_Self, _Receiver>
- {
- return {((_Self&&)__self).__sndr_, (_Receiver&&)__rcvr,
- ((_Self&&)__self).__env_};
- }
-
- friend auto tag_invoke(stdexec::get_env_t, const __t& __self) //
- noexcept(
- stdexec::__nothrow_callable<stdexec::get_env_t, const _Sender&>)
- -> stdexec::env_of_t<const _Sender&>
- {
- return stdexec::get_env(__self.__sndr_);
- }
-
- template <__decays_to<__t> _Self, class _BaseEnv>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _BaseEnv&&)
- -> stdexec::__completion_signatures_of_t<
- __copy_cvref_t<_Self, _Sender>,
- __env::__env_join_t<_Env, _BaseEnv>>;
- };
-};
-
-struct __write_t
-{
- template <class _Sender, class... _Funs>
- using __sender_t =
- __t<__sender<__id<__decay_t<_Sender>>,
- __env::__env_join_t<__env::__env_fn<_Funs>...>>>;
-
- template <__is_not_instance_of<__env::__env_fn> _Sender, class... _Funs>
- requires sender<_Sender>
- auto operator()(_Sender&& __sndr, __env::__env_fn<_Funs>... __withs) const
- -> __sender_t<_Sender, _Funs...>
- {
- return {(_Sender&&)__sndr, __env::__join_env(std::move(__withs)...)};
- }
-
- template <class... _Funs>
- auto operator()(__env::__env_fn<_Funs>... __withs) const
- -> __binder_back<__write_t, __env::__env_fn<_Funs>...>
- {
- return {{}, {}, {std::move(__withs)...}};
- }
-};
-} // namespace __write
-
-inline constexpr __write::__write_t write{};
+inline constexpr stdexec::__write_::__write_t write{};
} // namespace exec
-#ifdef __EDG__
-#pragma diagnostic pop
-#endif
+STDEXEC_PRAGMA_POP()
diff --git a/include/sdbusplus/async/stdexec/execution.hpp b/include/sdbusplus/async/stdexec/execution.hpp
index 1a81553..78a5fff 100644
--- a/include/sdbusplus/async/stdexec/execution.hpp
+++ b/include/sdbusplus/async/stdexec/execution.hpp
@@ -15,6 +15,7 @@
*/
#pragma once
+#include "__detail/__basic_sender.hpp"
#include "__detail/__execution_fwd.hpp"
#include "__detail/__intrusive_ptr.hpp"
#include "__detail/__meta.hpp"
@@ -37,53 +38,15 @@
#include <type_traits>
#include <variant>
-#ifdef __EDG__
-#pragma diagnostic push
-#pragma diag_suppress 1302
-#pragma diag_suppress 497
-#pragma diag_suppress type_qualifiers_ignored_on_reference
-#endif
-
-#ifndef STDEXEC_DISABLE_R5_DEPRECATION_WARNINGS
-#define STDEXEC_R5_SENDER_DEPRECATION_WARNING \
- [[deprecated( \
- "Deprecated sender type detected. " \
- "Please give the type a nested `is_sender` type alias, or " \
- "specialize stdexec::enable_sender<your-sender-type> to be `true`. " \
- "To suppress this deprecation warning, define `STDEXEC_DISABLE_R5_DEPRECATIONS`.")]]
-#define STDEXEC_R5_RECEIVER_DEPRECATION_WARNING \
- [[deprecated( \
- "Deprecated receiver type detected. " \
- "Please give the type a nested `is_receiver` type alias, or " \
- "specialize stdexec::enable_receiver<your-receiver-type> to be `true`." \
- "To suppress this deprecation warning, define `STDEXEC_DISABLE_R5_DEPRECATIONS`.")]]
-#define STDEXEC_R5_DEPENDENT_COMPLETION_SIGNATURES_DEPRECATION_WARNING \
- [[deprecated( \
- "The `dependent_completion_signatures<>` type is deprecated. There is " \
- "no need to define a customization of `get_completion_signatures` for " \
- "sender types whose completions are dependent on the receiver's environment. " \
- "Give your sender type a nested `is_sender` type alias instead, " \
- "specialize stdexec::enable_sender<your-sender-type> to be `true`. " \
- "To suppress this deprecation warning, define `STDEXEC_DISABLE_R5_DEPRECATIONS`.")]]
-#define STDEXEC_R5_NO_ENV_DEPRECATION_WARNING \
- [[deprecated( \
- "The `no_env` type is deprecated. The stdexec library no longer needs to use " \
- "it to probe a type for satisfaction of the `sender` concept. " \
- "Give your sender type a nested `is_sender` type alias instead, or " \
- "specialize stdexec::enable_sender<your-sender-type> to be `true`. " \
- "To suppress this deprecation warning, define `STDEXEC_DISABLE_R5_DEPRECATIONS`.")]]
-#else
-#define STDEXEC_R5_SENDER_DEPRECATION_WARNING
-#define STDEXEC_R5_RECEIVER_DEPRECATION_WARNING
-#define STDEXEC_R5_DEPENDENT_COMPLETION_SIGNATURES_DEPRECATION_WARNING
-#define STDEXEC_R5_NO_ENV_DEPRECATION_WARNING
-#endif
-
-#define STDEXEC_LEGACY_R5_CONCEPTS() 1
-
STDEXEC_PRAGMA_PUSH()
-STDEXEC_PRAGMA_IGNORE("-Wundefined-inline")
-STDEXEC_PRAGMA_IGNORE("-Wundefined-internal")
+STDEXEC_PRAGMA_IGNORE_GNU("-Wpragmas")
+STDEXEC_PRAGMA_IGNORE_GNU("-Wunknown-warning-option")
+STDEXEC_PRAGMA_IGNORE_GNU("-Wundefined-inline")
+STDEXEC_PRAGMA_IGNORE_GNU("-Wsubobject-linkage")
+
+STDEXEC_PRAGMA_IGNORE_EDG(1302)
+STDEXEC_PRAGMA_IGNORE_EDG(497)
+STDEXEC_PRAGMA_IGNORE_EDG(type_qualifiers_ignored_on_reference)
namespace stdexec
{
@@ -98,6 +61,152 @@
static inline constexpr Tag (*signature)(Sig) = nullptr;
};
+struct default_domain;
+struct dependent_domain;
+
+template <class _Sender>
+using tag_of_t = __tag_of<_Sender>;
+
+namespace __domain
+{
+template <class _Tag>
+using __legacy_c11n_for = typename _Tag::__legacy_customizations_t;
+
+template <class _Tag, class... _Args>
+using __legacy_c11n_fn = //
+ __make_dispatcher<__legacy_c11n_for<_Tag>, __none_such, _Args...>;
+
+template <class _Tag, class... _Args>
+concept __has_legacy_c11n = //
+ __callable<__legacy_c11n_fn<_Tag, _Args...>, _Args...>;
+
+struct __legacy_customization
+{
+ template <class _Tag, class _Data, class... _Children>
+ requires __has_legacy_c11n<_Tag, _Data, _Children...>
+ decltype(auto) operator()(_Tag, _Data && __data,
+ _Children&&... __children) const
+ {
+ return __legacy_c11n_fn<_Tag, _Data, _Children...>()(
+ static_cast<_Data&&>(__data),
+ static_cast<_Children&&>(__children)...);
+ }
+};
+
+template <class _DomainOrTag, class _Sender, class... _Env>
+concept __has_transform_sender =
+ requires(_DomainOrTag __tag, _Sender&& __sender, const _Env&... __env) {
+ __tag.transform_sender((_Sender&&)__sender, __env...);
+ };
+
+template <class _Sender, class... _Env>
+concept __has_default_transform_sender = //
+ sender_expr<_Sender> //
+ && __has_transform_sender<tag_of_t<_Sender>, _Sender, _Env...>;
+
+template <class _Type, class _Sender, class _Env>
+concept __has_transform_env =
+ requires(_Type __obj, _Sender&& __sender, _Env&& __env) {
+ __obj.transform_env((_Sender&&)__sender, (_Env&&)__env);
+ };
+
+template <class _Sender, class _Env>
+concept __has_default_transform_env = //
+ sender_expr<_Sender> //
+ && __has_transform_env<tag_of_t<_Sender>, _Sender, _Env>;
+
+template <class _DomainOrTag, class... _Args>
+concept __has_apply_sender = requires(_DomainOrTag __tag, _Args&&... __args) {
+ __tag.apply_sender((_Args&&)__args...);
+ };
+} // namespace __domain
+
+namespace __write_
+{
+struct __write_t;
+}
+
+struct default_domain
+{
+ default_domain() = default;
+
+ // Called without the environment during eager customization
+ template <class _Sender>
+ STDEXEC_ATTRIBUTE((always_inline))
+ decltype(auto) transform_sender(_Sender&& __sndr) const
+ {
+ // Look for a legacy customization for the given tag, and if found,
+ // apply it.
+ if constexpr (__callable<__sexpr_apply_t, _Sender,
+ __domain::__legacy_customization>)
+ {
+ return stdexec::__sexpr_apply((_Sender&&)__sndr,
+ __domain::__legacy_customization());
+ }
+ else if constexpr (__domain::__has_default_transform_sender<_Sender>)
+ {
+ return tag_of_t<_Sender>().transform_sender((_Sender&&)__sndr);
+ }
+ else
+ {
+ return static_cast<_Sender>((_Sender&&)__sndr);
+ }
+ STDEXEC_UNREACHABLE();
+ }
+
+ // Called with an environment during lazy customization
+ template <class _Sender, class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env) const
+ {
+ if constexpr (__domain::__has_default_transform_sender<_Sender, _Env>)
+ {
+ return tag_of_t<_Sender>().transform_sender((_Sender&&)__sndr,
+ __env);
+ }
+ else
+ {
+ return static_cast<_Sender>((_Sender&&)__sndr);
+ }
+ STDEXEC_UNREACHABLE();
+ }
+
+ template <class _Tag, class _Sender, class... _Args>
+ requires __domain::__has_legacy_c11n<_Tag, _Sender, _Args...> ||
+ __domain::__has_apply_sender<_Tag, _Sender, _Args...>
+ STDEXEC_ATTRIBUTE((always_inline)) decltype(auto)
+ apply_sender(_Tag, _Sender&& __sndr, _Args&&... __args) const
+ {
+ // Look for a legacy customization for the given tag, and if found,
+ // apply it.
+ if constexpr (__domain::__has_legacy_c11n<_Tag, _Sender, _Args...>)
+ {
+ return __domain::__legacy_c11n_fn<_Tag, _Sender, _Args...>()(
+ static_cast<_Sender&&>(__sndr),
+ static_cast<_Args&&>(__args)...);
+ }
+ else
+ {
+ return _Tag().apply_sender((_Sender&&)__sndr, (_Args&&)__args...);
+ }
+ STDEXEC_UNREACHABLE();
+ }
+
+ template <class _Sender, class _Env>
+ decltype(auto) transform_env(_Sender&& __sndr, _Env&& __env) const noexcept
+ {
+ if constexpr (__domain::__has_default_transform_env<_Sender, _Env>)
+ {
+ return tag_of_t<_Sender>().transform_env((_Sender&&)__sndr,
+ (_Env&&)__env);
+ }
+ else
+ {
+ return static_cast<_Env>((_Env&&)__env);
+ }
+ }
+};
+
//////////////////////////////////////////////////////////////////////////////////////////////////
// [exec.queries]
namespace __queries
@@ -122,6 +231,26 @@
}
};
+struct query_or_t
+{
+ template <class _Query, class _Queryable, class _Default>
+ constexpr auto operator()(_Query, _Queryable&&, _Default&& __default) const
+ noexcept(__nothrow_constructible_from<_Default, _Default&&>) -> _Default
+ {
+ return (_Default&&)__default;
+ }
+
+ template <class _Query, class _Queryable, class _Default>
+ requires __callable<_Query, _Queryable>
+ constexpr auto operator()(_Query __query, _Queryable&& __queryable,
+ _Default&&) const
+ noexcept(__nothrow_callable<_Query, _Queryable>)
+ -> __call_result_t<_Query, _Queryable>
+ {
+ return ((_Query&&)__query)((_Queryable&&)__queryable);
+ }
+};
+
struct execute_may_block_caller_t : __query<execute_may_block_caller_t>
{
template <class _Tp>
@@ -190,7 +319,7 @@
// TODO: implement allocator concept
template <class _T0>
-concept __allocator = true;
+concept __allocator_c = true;
struct get_scheduler_t : __query<get_scheduler_t>
{
@@ -239,7 +368,7 @@
{
static_assert(nothrow_tag_invocable<get_allocator_t, const _Env&>);
static_assert(
- __allocator<tag_invoke_result_t<get_allocator_t, const _Env&>>);
+ __allocator_c<tag_invoke_result_t<get_allocator_t, const _Env&>>);
return tag_invoke(get_allocator_t{}, __env);
}
@@ -294,6 +423,26 @@
-> tag_invoke_result_t<get_completion_scheduler_t<_CPO>,
const _Queryable&>;
};
+
+struct get_domain_t
+{
+ template <class _Ty>
+ requires tag_invocable<get_domain_t, const _Ty&>
+ constexpr auto operator()(const _Ty& __ty) const noexcept
+ -> tag_invoke_result_t<get_domain_t, const _Ty&>
+ {
+ static_assert(nothrow_tag_invocable<get_domain_t, const _Ty&>,
+ "Customizations of get_domain must be noexcept.");
+ static_assert(__class<tag_invoke_result_t<get_domain_t, const _Ty&>>,
+ "Customizations of get_domain must return a class type.");
+ return tag_invoke(get_domain_t{}, __ty);
+ }
+
+ friend constexpr bool tag_invoke(forwarding_query_t, get_domain_t) noexcept
+ {
+ return true;
+ }
+};
} // namespace __queries
using __queries::__has_algorithm_customizations_t;
@@ -302,11 +451,14 @@
using __queries::get_allocator_t;
using __queries::get_completion_scheduler_t;
using __queries::get_delegatee_scheduler_t;
+using __queries::get_domain_t;
using __queries::get_forward_progress_guarantee_t;
using __queries::get_scheduler_t;
using __queries::get_stop_token_t;
+using __queries::query_or_t;
inline constexpr forwarding_query_t forwarding_query{};
+inline constexpr query_or_t query_or{}; // NOT TO SPEC
inline constexpr execute_may_block_caller_t execute_may_block_caller{};
inline constexpr __has_algorithm_customizations_t
__has_algorithm_customizations{};
@@ -316,7 +468,7 @@
inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler{};
inline constexpr get_allocator_t get_allocator{};
inline constexpr get_stop_token_t get_stop_token{};
-#if !STDEXEC_GCC() || defined(__OPTIMIZE__)
+#if !STDEXEC_GCC() || defined(__OPTIMIZE_SIZE__)
template <__completion_tag _CPO>
inline constexpr get_completion_scheduler_t<_CPO> get_completion_scheduler{};
#else
@@ -334,34 +486,74 @@
template <class _Tag>
concept __forwarding_query = forwarding_query(_Tag{});
+inline constexpr get_domain_t get_domain{};
+
+template <class _Tag, class _Queryable, class _Default>
+using __query_result_or_t =
+ __call_result_t<query_or_t, _Tag, _Queryable, _Default>;
+
/////////////////////////////////////////////////////////////////////////////
// env_of
namespace __env
{
-struct no_env
-{
- using __t = no_env;
- using __id = no_env;
- template <class _Tag, same_as<no_env> _Self, class... _Ts>
- friend void tag_invoke(_Tag, _Self, _Ts&&...) = delete;
-};
-
struct empty_env
{
using __t = empty_env;
using __id = empty_env;
};
-template <class _Tag>
-struct __deleted
-{};
+template <class _Descriptor>
+struct __prop;
+
+template <class _Value, class... _Tags>
+struct __prop<_Value(_Tags...)>
+{
+ using __t = __prop;
+ using __id = __prop;
+ _Value __value_;
+
+ template <__one_of<_Tags...> _Key>
+ friend auto tag_invoke(_Key, const __prop& __self) //
+ noexcept(__nothrow_decay_copyable<_Value>) -> _Value
+ {
+ return __self.__value_;
+ }
+};
+
+template <class... _Tags>
+struct __prop<void(_Tags...)>
+{
+ using __t = __prop;
+ using __id = __prop;
+
+ template <__one_of<_Tags...> _Key, class _Self>
+ requires(std::is_base_of_v<__prop, __decay_t<_Self>>)
+ friend auto tag_invoke(_Key, _Self&&) noexcept = delete;
+};
+
+struct __mkprop_t
+{
+ template <class _Value, class _Tag, class... _Tags>
+ auto operator()(_Value&& __value, _Tag, _Tags...) const
+ noexcept(__nothrow_decay_copyable<_Value>)
+ -> __prop<__decay_t<_Value>(_Tag, _Tags...)>
+ {
+ return {(_Value&&)__value};
+ }
+
+ template <class _Tag>
+ auto operator()(_Tag) const -> __prop<void(_Tag)>
+ {
+ return {};
+ }
+};
template <__nothrow_move_constructible _Fun>
struct __env_fn
{
using __t = __env_fn;
using __id = __env_fn;
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_;
template <class _Tag>
requires __callable<const _Fun&, _Tag>
@@ -376,12 +568,13 @@
template <class _Fun>
__env_fn(_Fun) -> __env_fn<_Fun>;
-template <__nothrow_move_constructible _Env>
+template <class _Env>
struct __env_fwd
{
+ static_assert(__nothrow_move_constructible<_Env>);
using __t = __env_fwd;
using __id = __env_fwd;
- STDEXEC_NO_UNIQUE_ADDRESS _Env __env_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_;
template <__forwarding_query _Tag>
requires tag_invocable<_Tag, const _Env&>
@@ -393,13 +586,16 @@
}
};
-template <__nothrow_move_constructible _Env,
- __nothrow_move_constructible _Base = empty_env>
-struct __env_join : __env_fwd<_Base>
+template <class _Env>
+__env_fwd(_Env&&) -> __env_fwd<_Env>;
+
+template <class _Env, class _Base = empty_env>
+struct __joined_env : __env_fwd<_Base>
{
- using __t = __env_join;
- using __id = __env_join;
- STDEXEC_NO_UNIQUE_ADDRESS _Env __env_;
+ static_assert(__nothrow_move_constructible<_Env>);
+ using __t = __joined_env;
+ using __id = __joined_env;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_;
const _Base& base() const noexcept
{
@@ -408,7 +604,7 @@
template <class _Tag>
requires tag_invocable<_Tag, const _Env&>
- friend auto tag_invoke(_Tag, const __env_join& __self) //
+ friend auto tag_invoke(_Tag, const __joined_env& __self) //
noexcept(nothrow_tag_invocable<_Tag, const _Env&>)
-> tag_invoke_result_t<_Tag, const _Env&>
{
@@ -417,67 +613,56 @@
};
template <class _Tag, class _Base>
-struct __env_join<__env_fn<__deleted<_Tag>>, _Base> : __env_fwd<_Base>
+struct __joined_env<__prop<void(_Tag)>, _Base> : __env_fwd<_Base>
{
- using __t = __env_join;
- using __id = __env_join;
- STDEXEC_NO_UNIQUE_ADDRESS __env_fn<__deleted<_Tag>> __env_;
+ using __t = __joined_env;
+ using __id = __joined_env;
+ STDEXEC_ATTRIBUTE((no_unique_address)) __prop<void(_Tag)> __env_;
- friend void tag_invoke(_Tag, const __env_join&) noexcept = delete;
+ friend void tag_invoke(_Tag, const __joined_env&) noexcept = delete;
};
-template <class _Env>
-_Env __join_env(_Env&& __env) noexcept
+struct __join_env_t
{
- return (_Env&&)__env;
-}
+ template <class _Env>
+ _Env operator()(_Env&& __env) const noexcept
+ {
+ return (_Env&&)__env;
+ }
-template <class _Env, class _Base>
-__env_join<_Env, _Base> __join_env(_Env&& __env, _Base&& __base) noexcept
-{
- static_assert(!same_as<__decay_t<_Env>, no_env>);
- return {{{(_Base&&)__base}}, (_Env&&)__env};
-}
+ template <class _Env, class _Base>
+ decltype(auto) operator()(_Env && __env, _Base && __base) const noexcept
+ {
+ using __env_t = __decay_t<_Env>;
+ using __base_t = __decay_t<_Base>;
+ if constexpr (__same_as<__env_t, empty_env>)
+ {
+ return _Base((_Base&&)__base);
+ }
+ else if constexpr (__same_as<__base_t, empty_env>)
+ {
+ return _Env((_Env&&)__env);
+ }
+ else
+ {
+ return __joined_env<_Env, _Base>{{(_Base&&)__base}, (_Env&&)__env};
+ }
+ }
-template <class _Base>
-_Base __join_env(empty_env, _Base&& __base) noexcept
-{
- return (_Base&&)__base;
-}
-
-template <class _Env>
-_Env __join_env(_Env&& __env, empty_env) noexcept
-{
- static_assert(!same_as<__decay_t<_Env>, no_env>);
- return (_Env&&)__env;
-}
-
-inline empty_env __join_env(empty_env, empty_env) noexcept
-{
- return {};
-}
-
-template <class _Env>
-no_env __join_env(_Env&&, no_env) noexcept
- requires true
-{
- static_assert(!same_as<__decay_t<_Env>, no_env>);
- return {};
-}
-
-template <class _Env0, class _Env1, class _Env2, class... _Envs>
-auto __join_env(_Env0&& __env0, _Env1&& __env1, _Env2&& __env2,
- _Envs&&... __envs) noexcept
-{
- return __env::__join_env(
- (_Env0&&)__env0,
- __env::__join_env(
- (_Env1&&)__env1,
- __env::__join_env((_Env2&&)__env2, (_Envs&&)__envs...)));
-}
+ template <class _Env0, class _Env1, class _Env2, class... _Envs>
+ decltype(auto) operator()(_Env0 && __env0, _Env1 && __env1, _Env2 && __env2,
+ _Envs&&... __envs) const noexcept
+ {
+ const auto& __join_env = *this;
+ return __join_env(
+ (_Env0&&)__env0,
+ __join_env((_Env1&&)__env1,
+ __join_env((_Env2&&)__env2, (_Envs&&)__envs...)));
+ }
+};
template <class... _Envs>
-using __env_join_t = decltype(__env::__join_env(__declval<_Envs>()...));
+using __env_join_t = __call_result_t<__join_env_t, _Envs...>;
// To be kept in sync with the promise type used in __connect_awaitable
template <class _Env>
@@ -502,23 +687,6 @@
-> const _Env&;
};
-template <class _Tag, __nothrow_move_constructible _Value>
-constexpr auto __with_(_Tag, _Value __val) noexcept
-{
- return __env_fn{
- [__val = std::move(__val)](_Tag) noexcept(
- __nothrow_copy_constructible<_Value>) { return __val; }};
-}
-
-template <class _Tag>
-__env_fn<__deleted<_Tag>> __with_(_Tag) noexcept
-{
- return {};
-}
-
-template <class... _Ts>
-using __with = decltype(__env::__with_(__declval<_Ts>()...));
-
// For making an environment from key/value pairs and optionally
// another environment.
struct __make_env_t
@@ -528,7 +696,7 @@
auto operator()(_Base&& __base, _Env&& __env) const noexcept
-> __env_join_t<_Env, _Base>
{
- return stdexec::__env::__join_env((_Env&&)__env, (_Base&&)__base);
+ return __join_env_t()((_Env&&)__env, (_Base&&)__base);
}
template <__nothrow_move_constructible _Env>
@@ -543,7 +711,9 @@
{
template <class _EnvProvider>
requires tag_invocable<get_env_t, const _EnvProvider&>
- constexpr auto operator()(const _EnvProvider& __with_env) const noexcept
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto
+ operator()(const _EnvProvider& __with_env) const noexcept
-> tag_invoke_result_t<get_env_t, const _EnvProvider&>
{
static_assert(
@@ -552,48 +722,34 @@
return tag_invoke(*this, __with_env);
}
- // NOT TO SPEC: The overload below checks the non-standard
- // enable_sender to determine whether to provide backwards
- // compatible behavior for R5 version sender types. When we
- // deprecate R5 support, we can bring this overload in line with
- // P2300R7.
template <class _EnvProvider>
- constexpr decltype(auto)
- operator()(const _EnvProvider & __with_env) const noexcept
+ constexpr empty_env operator()(const _EnvProvider&) const noexcept
{
- if constexpr (!enable_sender<_EnvProvider>)
- {
- return __with_env;
- }
- else
- {
- return empty_env{};
- }
+ return {};
}
};
} // namespace __env
using __env::empty_env;
-using __env::no_env;
using __empty_env
[[deprecated("Please use stdexec::empty_env now.")]] = empty_env;
using __env::__env_promise;
-using __env::__with;
-using __env::__with_;
-using no_env_promise = __env_promise<no_env>;
inline constexpr __env::__make_env_t __make_env{};
+inline constexpr __env::__join_env_t __join_env{};
inline constexpr __env::get_env_t get_env{};
-template <class... _Ts>
-using __make_env_t = decltype(__make_env(__declval<_Ts>()...));
+// for making an environment from a single key/value pair
+inline constexpr __env::__mkprop_t __mkprop{};
-#if STDEXEC_LEGACY_R5_CONCEPTS()
-using __default_env = no_env;
-#else
+template <class _Tag, class _Value = void>
+using __with = __env::__prop<_Value(_Tag)>;
+
+template <class... _Ts>
+using __make_env_t = __call_result_t<__env::__make_env_t, _Ts...>;
+
using __default_env = empty_env;
-#endif
template <class _EnvProvider>
concept environment_provider = //
@@ -601,14 +757,195 @@
{
get_env(std::as_const(__ep))
} -> queryable;
- // NOT TO SPEC: Remove the following line when we deprecate all
- // R5 entities.
- {
- get_env(std::as_const(__ep))
- } -> __none_of<no_env, void>;
};
/////////////////////////////////////////////////////////////////////////////
+template <class _Sender, class _Scheduler, class _Tag = set_value_t>
+concept __completes_on = __decays_to<
+ __call_result_t<get_completion_scheduler_t<_Tag>, env_of_t<_Sender>>,
+ _Scheduler>;
+
+/////////////////////////////////////////////////////////////////////////////
+template <class _Sender, class _Scheduler, class _Env>
+concept __starts_on =
+ __decays_to<__call_result_t<get_scheduler_t, _Env>, _Scheduler>;
+
+/////////////////////////////////////////////////////////////////////////////
+namespace __detail
+{
+template <class _Env, class _Tag>
+using __completion_scheduler_for =
+ __meval_or<__call_result_t, __none_such, get_completion_scheduler_t<_Tag>,
+ _Env>;
+
+template <class _Env, class _Tag>
+using __completion_domain_for =
+ __meval_or<__call_result_t, __none_such, get_domain_t,
+ __completion_scheduler_for<_Env, _Tag>>;
+
+// Check the value, error, and stopped channels for completion schedulers.
+// Of the completion schedulers that are known, they must all have compatible
+// domains. This computes that domain, or else returns __none_such if there
+// are no completion schedulers or if they don't specify a domain.
+template <class _Env>
+struct __completion_domain_or_none_ :
+ __mdefer_<__transform<
+ __mbind_front_q<__completion_domain_for, _Env>,
+ __remove<__none_such, __munique<__msingle_or<__none_such>>>>,
+ set_value_t, set_error_t, set_stopped_t>
+{};
+
+template <class _Sender>
+using __completion_domain_or_none =
+ __t<__completion_domain_or_none_<env_of_t<_Sender>>>;
+
+template <class _Sender>
+concept __consistent_completion_domains =
+ __mvalid<__completion_domain_or_none, _Sender>;
+
+template <class _Sender>
+concept __has_completion_domain =
+ (!same_as<__completion_domain_or_none<_Sender>, __none_such>);
+
+template <__has_completion_domain _Sender>
+using __completion_domain_of = __completion_domain_or_none<_Sender>;
+} // namespace __detail
+
+/////////////////////////////////////////////////////////////////////////////
+inline constexpr struct __get_early_domain_t
+{
+ template <class _Sender, class _Default = default_domain>
+ auto operator()(const _Sender&, _Default __def = {}) const noexcept
+ {
+ if constexpr (__callable<get_domain_t, env_of_t<_Sender>>)
+ {
+ return __call_result_t<get_domain_t, env_of_t<_Sender>>();
+ }
+ else if constexpr (__detail::__has_completion_domain<_Sender>)
+ {
+ return __detail::__completion_domain_of<_Sender>();
+ }
+ else
+ {
+ return __def;
+ }
+ STDEXEC_UNREACHABLE();
+ }
+} __get_early_domain{};
+
+template <class _Sender, class _Default = default_domain>
+using __early_domain_of_t =
+ __call_result_t<__get_early_domain_t, _Sender, _Default>;
+
+/////////////////////////////////////////////////////////////////////////////
+inline constexpr struct __get_late_domain_t
+{
+ // When connect is looking for a customization, it first checks the sender's
+ // domain. If the sender knows the domain in which it completes, then that
+ // is where the subsequent task will execute. Otherwise, look to the
+ // receiver for late-bound information about the current execution context.
+ template <class _Sender, class _Env>
+ auto operator()(const _Sender& __sndr, const _Env& __env) const noexcept
+ {
+ if constexpr (!same_as<dependent_domain,
+ __early_domain_of_t<_Sender, dependent_domain>>)
+ {
+ return __get_early_domain(__sndr);
+ }
+ else if constexpr (__callable<get_domain_t, const _Env&>)
+ {
+ return get_domain(__env);
+ }
+ else if constexpr (__callable<__composed<get_domain_t, get_scheduler_t>,
+ const _Env&>)
+ {
+ return get_domain(get_scheduler(__env));
+ }
+ else
+ {
+ return default_domain();
+ }
+ STDEXEC_UNREACHABLE();
+ }
+
+ // The transfer algorithm is the exception to the rule. It ignores the
+ // domain of the predecessor, and dispatches based on the domain of the
+ // scheduler to which execution is being transferred.
+ template <sender_expr_for<transfer_t> _Sender, class _Env>
+ auto operator()(const _Sender& __sndr, const _Env&) const noexcept
+ {
+ return __sexpr_apply(__sndr,
+ [](__ignore, auto& __data, __ignore) noexcept {
+ auto __sched = get_completion_scheduler<set_value_t>(__data);
+ return query_or(get_domain, __sched, default_domain());
+ });
+ }
+} __get_late_domain{};
+
+template <class _Sender, class _Env>
+using __late_domain_of_t = __call_result_t<__get_late_domain_t, _Sender, _Env>;
+
+namespace __domain
+{
+struct __common_domain_fn
+{
+ static default_domain __common_domain() noexcept
+ {
+ return {};
+ }
+
+ template <class _Domain, class... _OtherDomains>
+ requires __all_of<_Domain, _OtherDomains...>
+ static _Domain __common_domain(_Domain __domain, _OtherDomains...) noexcept
+ {
+ return (_Domain&&)__domain;
+ }
+
+ template <class... _Domains>
+ static auto __common_domain(_Domains...) noexcept //
+ -> __if_c<__one_of<dependent_domain, _Domains...>, dependent_domain,
+ __none_such>
+ {
+ return {};
+ }
+
+ auto operator()(__ignore, __ignore, const auto&... __sndrs) const noexcept
+ {
+ return __common_domain(__get_early_domain(__sndrs)...);
+ }
+};
+
+template <class... _Senders>
+using __common_domain_t = //
+ __call_result_t<__common_domain_fn, int, int, _Senders...>;
+
+template <class... _Senders>
+concept __has_common_domain = //
+ __none_of<__none_such, __common_domain_t<_Senders...>>;
+
+template <class _Tag>
+struct __get_env_common_domain
+{
+ template <sender_expr_for<_Tag> _Self>
+ static auto get_env(const _Self& __self) noexcept
+ {
+ using _Domain =
+ __call_result_t<__sexpr_apply_t, const _Self&, __common_domain_fn>;
+ if constexpr (same_as<_Domain, default_domain>)
+ {
+ return empty_env();
+ }
+ else
+ {
+ return __mkprop(__sexpr_apply(__self, __common_domain_fn()),
+ get_domain);
+ }
+ STDEXEC_UNREACHABLE();
+ }
+};
+} // namespace __domain
+
+/////////////////////////////////////////////////////////////////////////////
// [execution.receivers]
namespace __receivers
{
@@ -619,7 +956,7 @@
template <class _Receiver, class... _As>
requires tag_invocable<set_value_t, _Receiver, _As...>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) //
void
operator()(_Receiver&& __rcvr, _As&&... __as) const noexcept
{
@@ -636,7 +973,7 @@
template <class _Receiver, class _Error>
requires tag_invocable<set_error_t, _Receiver, _Error>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) //
void
operator()(_Receiver&& __rcvr, _Error&& __err) const noexcept
{
@@ -653,7 +990,7 @@
template <class _Receiver>
requires tag_invocable<set_stopped_t, _Receiver>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) //
void
operator()(_Receiver&& __rcvr) const noexcept
{
@@ -695,6 +1032,46 @@
}
} __try_call{};
+namespace __error__
+{
+inline constexpr __mstring __unrecognized_sender_type_diagnostic =
+ "The given type cannot be used as a sender with the given environment "
+ "because the attempt to compute the completion signatures failed."__csz;
+
+template <__mstring _Diagnostic = __unrecognized_sender_type_diagnostic>
+struct _UNRECOGNIZED_SENDER_TYPE_;
+
+template <class _Sender>
+struct _WITH_SENDER_;
+
+template <class... _Senders>
+struct _WITH_SENDERS_;
+
+template <class _Env>
+struct _WITH_ENVIRONMENT_;
+
+template <class _Ty>
+struct _WITH_TYPE_;
+
+template <class _Receiver>
+struct _WITH_RECEIVER_;
+
+template <class _Sig>
+struct _MISSING_COMPLETION_SIGNAL_;
+} // namespace __error__
+
+using __error__::_MISSING_COMPLETION_SIGNAL_;
+using __error__::_UNRECOGNIZED_SENDER_TYPE_;
+using __error__::_WITH_ENVIRONMENT_;
+using __error__::_WITH_RECEIVER_;
+using __error__::_WITH_TYPE_;
+
+template <class _Sender>
+using _WITH_SENDER_ = __error__::_WITH_SENDER_<__name_of<_Sender>>;
+
+template <class... _Senders>
+using _WITH_SENDERS_ = __error__::_WITH_SENDERS_<__name_of<_Senders>...>;
+
/////////////////////////////////////////////////////////////////////////////
// completion_signatures
namespace __compl_sigs
@@ -734,17 +1111,6 @@
void __test(_Tag (*)(_Args...) noexcept) = delete;
#endif
-// BUGBUG not to spec!
-struct __dependent
-{
-#if !STDEXEC_STD_NO_COROUTINES_
- bool await_ready();
- template <class _Env>
- void await_suspend(__coro::coroutine_handle<__env_promise<_Env>>);
- __dependent await_resume();
-#endif
-};
-
#if STDEXEC_NVHPC()
template <class _Sig>
concept __completion_signature = __compl_sigs::__is_compl_sig<_Sig>;
@@ -765,9 +1131,6 @@
using __compl_sigs::__completion_signature;
-template <same_as<no_env>>
-using dependent_completion_signatures = __compl_sigs::__dependent;
-
template <__compl_sigs::__completion_signature... _Sigs>
struct completion_signatures
{
@@ -803,22 +1166,33 @@
__meval<__for_all_sigs, _Completions, _TaggedTuple, _Variant>;
} // namespace __compl_sigs
-template <class _Ty>
-concept __is_completion_signatures =
- __is_instance_of<_Ty, completion_signatures>;
+template <class _Completions>
+concept __valid_completion_signatures = //
+ __is_instance_of<_Completions, completion_signatures>;
-template <class...>
-auto __concat_completion_signatures_impl() //
- -> dependent_completion_signatures<no_env>;
+template <class _Completions>
+using __invalid_completion_signatures_t = //
+ __mbool<!__valid_completion_signatures<_Completions>>;
-template <__is_completion_signatures... _Completions>
-auto __concat_completion_signatures_impl()
- -> __minvoke<__mconcat<__munique<__q<completion_signatures>>>,
- _Completions...>;
+template <__mstring _Msg =
+ "Expected an instance of template completion_signatures<>"__csz>
+struct _INVALID_COMPLETION_SIGNATURES_TYPE_
+{
+ template <class... _Completions>
+ using __f = //
+ __mexception<
+ _INVALID_COMPLETION_SIGNATURES_TYPE_<>,
+ __minvoke<__mfind_if<__q<__invalid_completion_signatures_t>,
+ __mcompose<__q<_WITH_TYPE_>, __q<__mfront>>>,
+ _Completions...>>;
+};
template <class... _Completions>
using __concat_completion_signatures_impl_t = //
- decltype(__concat_completion_signatures_impl<_Completions...>());
+ __minvoke<__if_c<(__valid_completion_signatures<_Completions> && ...),
+ __mconcat<__munique<__q<completion_signatures>>>,
+ _INVALID_COMPLETION_SIGNATURES_TYPE_<>>,
+ _Completions...>;
template <class... _Completions>
struct __concat_completion_signatures_
@@ -830,32 +1204,8 @@
using __concat_completion_signatures_t =
__t<__concat_completion_signatures_<_Completions...>>;
-template <class _Completions, class _Env>
-inline constexpr bool __expecting_completion_signatures = false;
-
-template <class... _Sigs, class _Env>
-inline constexpr bool
- __expecting_completion_signatures<completion_signatures<_Sigs...>, _Env> =
- true;
-
-template <>
-inline constexpr bool __expecting_completion_signatures<
- dependent_completion_signatures<no_env>, no_env> = true;
-
-template <class _Completions, class _Env>
-concept __valid_completion_signatures =
- __expecting_completion_signatures<_Completions, _Env>;
-
/////////////////////////////////////////////////////////////////////////////
// [execution.receivers]
-template <class _Receiver>
-struct _WITH_RECEIVER_
-{};
-
-template <class _Sig>
-struct _MISSING_COMPLETION_SIGNAL_
-{};
-
template <class _Receiver, class _Tag, class... _Args>
auto __try_completion(_Tag (*)(_Args...))
-> __mexception<_MISSING_COMPLETION_SIGNAL_<_Tag(_Args...)>,
@@ -870,6 +1220,15 @@
-> decltype((__msuccess(), ...,
stdexec::__try_completion<_Receiver>((_Sigs*)nullptr)));
+template <class _Sender, class _Env>
+using __unrecognized_sender_error = //
+ __mexception<_UNRECOGNIZED_SENDER_TYPE_<>, _WITH_SENDER_<_Sender>,
+ _WITH_ENVIRONMENT_<_Env>>;
+
+template <class _Sender, class _Env>
+using __completion_signatures_of_t =
+ __call_result_t<get_completion_signatures_t, _Sender, _Env>;
+
/////////////////////////////////////////////////////////////////////////////
// [execution.receivers]
struct __receiver_base
@@ -884,25 +1243,8 @@
inline constexpr bool enable_receiver =
__enable_receiver<_Receiver>; // NOT TO SPEC
-// NOT TO SPEC:
-// As we upgrade the receiver related entities from R5 to R7,
-// we allow types that do not yet satisfy enable_receiver to
-// still satisfy the receiver concept if the type provides an
-// explicit get_env. All R5 receivers provided an explicit get_env,
-// so this is backwards compatible.
template <class _Receiver>
-concept __receiver_r5_or_r7 = //
- enable_receiver<_Receiver> //
- || tag_invocable<get_env_t, _Receiver>;
-
-template <class _Receiver>
-concept __receiver = //
- // Nested requirement here is to make this an atomic
- // constraint
- requires { requires __receiver_r5_or_r7<__decay_t<_Receiver>>; };
-
-template <class _Receiver>
-concept receiver = __receiver<_Receiver> && //
+concept receiver = enable_receiver<__decay_t<_Receiver>> && //
environment_provider<__cref_t<_Receiver>> && //
move_constructible<__decay_t<_Receiver>> && //
constructible_from<__decay_t<_Receiver>, _Receiver>;
@@ -946,6 +1288,24 @@
struct __completion_signatures
{};
+#if STDEXEC_MSVC()
+// MSVCBUG
+// https://developercommunity.visualstudio.com/t/Explicit-variable-template-specialisatio/10360032
+// MSVCBUG
+// https://developercommunity.visualstudio.com/t/Non-function-type-interpreted-as-functio/10447831
+
+template <class _Sig>
+struct __normalize_sig;
+
+template <class _Tag, class... _Args>
+struct __normalize_sig<_Tag(_Args...)>
+{
+ using __type = _Tag (*)(_Args&&...);
+};
+
+template <class _Sig>
+using __normalize_sig_t = typename __normalize_sig<_Sig>::__type;
+#else
template <class _Sig>
extern int __normalize_sig;
@@ -954,6 +1314,7 @@
template <class _Sig>
using __normalize_sig_t = decltype(__normalize_sig<_Sig>);
+#endif
template <class... _Sigs>
struct __valid_completions
@@ -961,9 +1322,9 @@
template <derived_from<__valid_completions> _Self, class _Tag,
class... _Args>
requires __one_of<_Tag (*)(_Args&&...), _Sigs...>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- friend void
- tag_invoke(_Tag, _Self&&, _Args&&...) noexcept
+ STDEXEC_ATTRIBUTE((host,
+ device)) friend void tag_invoke(_Tag, _Self&&,
+ _Args&&...) noexcept
{
STDEXEC_TERMINATE();
}
@@ -987,9 +1348,8 @@
using is_receiver = void;
template <same_as<get_env_t> _Tag>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- friend __debug_env_t<_Env>
- tag_invoke(_Tag, __debug_receiver) noexcept
+ STDEXEC_ATTRIBUTE((host, device))
+ friend __debug_env_t<_Env> tag_invoke(_Tag, __debug_receiver) noexcept
{
STDEXEC_TERMINATE();
}
@@ -1014,9 +1374,8 @@
[[deprecated(
"The sender claims to send a particular set of completions,"
" but in actual fact it completes with a result that is not"
- " one of the declared completion signatures.")]] STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- void
- _ATTENTION_() noexcept
+ " one of the declared completion signatures.")]] STDEXEC_ATTRIBUTE((host,
+ device)) void _ATTENTION_() noexcept
{}
template <class _Sig>
@@ -1025,6 +1384,9 @@
struct __t
{
template <class _CvrefSenderId, class _Env, class... _Sigs>
+ // BUGBUG this works around a recently (aug 2023) introduced regression
+ // in nvc++
+ requires(!__one_of<_Sig, _Sigs...>)
__t(__debug_receiver<_CvrefSenderId, _Env,
completion_signatures<_Sigs...>>&&) noexcept
{
@@ -1034,17 +1396,16 @@
_WARNING_< //
_COMPLETION_SIGNATURES_MISMATCH_,
_COMPLETION_SIGNATURE_<_Sig>, _IS_NOT_ONE_OF_<_Sigs...>,
- _SIGNAL_SENT_BY_SENDER_<_Sender>>;
+ _SIGNAL_SENT_BY_SENDER_<__name_of<_Sender>>>;
__debug::_ATTENTION_<_What>();
}
};
};
template <__completion_tag _Tag, class... _Args>
-STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- void
- tag_invoke(_Tag, __t<__invalid_completion<_Tag(_Args...)>>,
- _Args&&...) noexcept
+STDEXEC_ATTRIBUTE((host, device))
+void tag_invoke(_Tag, __t<__invalid_completion<_Tag(_Args...)>>,
+ _Args&&...) noexcept
{}
struct __debug_operation
@@ -1101,7 +1462,7 @@
template <class _Sigs, class _Env = empty_env, class _Sender>
void __debug_sender(_Sender&& __sndr, const _Env& = {})
{
- if constexpr (!__is_debug_env<_Env> && !same_as<_Env, no_env>)
+ if constexpr (!__is_debug_env<_Env>)
{
if (sizeof(_Sender) == ~0)
{ // never true
@@ -1121,7 +1482,7 @@
template <class _Env = empty_env, class _Sender>
void __debug_sender(_Sender&& __sndr, const _Env& = {})
{
- if constexpr (!__is_debug_env<_Env> && !same_as<_Env, no_env>)
+ if constexpr (!__is_debug_env<_Env>)
{
if (sizeof(_Sender) == ~0)
{ // never true
@@ -1148,76 +1509,263 @@
using __debug::__is_debug_env;
/////////////////////////////////////////////////////////////////////////////
+// dependent_domain
+struct dependent_domain
+{
+ template <sender_expr _Sender, class _Env>
+ requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
+ STDEXEC_ATTRIBUTE((always_inline)) decltype(auto)
+ transform_sender(_Sender&& __sndr, const _Env& __env) const;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.transform_sender]
+namespace __domain
+{
+struct __transform_env
+{
+ template <class _Domain, class _Sender, class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ /*constexpr*/ decltype(auto) operator()(_Domain __dom, _Sender && __sndr,
+ _Env && __env) const noexcept
+ {
+ if constexpr (__domain::__has_transform_env<_Domain, _Sender, _Env>)
+ {
+ return __dom.transform_env((_Sender&&)__sndr, (_Env&&)__env);
+ }
+ else
+ {
+ return default_domain().transform_env((_Sender&&)__sndr,
+ (_Env&&)__env);
+ }
+ }
+};
+
+struct __transform_sender_1
+{
+ template <class _Domain, class _Sender, class... _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ /*constexpr*/ decltype(auto) operator()(_Domain __dom, _Sender && __sndr,
+ const _Env&... __env) const
+ {
+ if constexpr (__domain::__has_transform_sender<_Domain, _Sender,
+ _Env...>)
+ {
+ return __dom.transform_sender((_Sender&&)__sndr, __env...);
+ }
+ else
+ {
+ return default_domain().transform_sender((_Sender&&)__sndr,
+ __env...);
+ }
+ }
+};
+
+template <class _Ty, class _Uy>
+concept __decay_same_as = same_as<__decay_t<_Ty>, __decay_t<_Uy>>;
+
+struct __transform_sender
+{
+ template <class _Self = __transform_sender, class _Domain, class _Sender,
+ class... _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ /*constexpr*/ decltype(auto) operator()(_Domain __dom, _Sender && __sndr,
+ const _Env&... __env) const
+ {
+ using _Sender2 = __call_result_t<__transform_sender_1, _Domain, _Sender,
+ const _Env&...>;
+ // If the transformation doesn't change the sender's type, then do not
+ // apply the transform recursively.
+ if constexpr (__decay_same_as<_Sender, _Sender2>)
+ {
+ return __transform_sender_1()(__dom, (_Sender&&)__sndr, __env...);
+ }
+ else
+ {
+ // We transformed the sender and got back a different sender.
+ // Transform that one too.
+ return _Self()(
+ __dom,
+ __transform_sender_1()(__dom, (_Sender&&)__sndr, __env...),
+ __env...);
+ }
+ }
+};
+
+struct __transform_dependent_sender
+{
+ // If we are doing a lazy customization of a type whose domain is
+ // value-dependent (e.g., let_value), first transform the sender to
+ // determine the domain. Then continue transforming the sender with the
+ // requested domain.
+ template <class _Domain, sender_expr _Sender, class _Env>
+ requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
+ /*constexpr*/ decltype(auto) operator()(_Domain __dom, _Sender && __sndr,
+ const _Env & __env) const
+ {
+ static_assert(__none_of<_Domain, dependent_domain>);
+ return __transform_sender()(
+ __dom,
+ dependent_domain().transform_sender((_Sender&&)__sndr, __env),
+ __env);
+ }
+};
+} // namespace __domain
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.transform_sender]
+inline constexpr struct transform_sender_t :
+ __domain::__transform_sender,
+ __domain::__transform_dependent_sender
+{
+ using __domain::__transform_sender::operator();
+ using __domain::__transform_dependent_sender::operator();
+} transform_sender{};
+
+template <class _Domain, class _Sender, class... _Env>
+using transform_sender_result_t =
+ __call_result_t<transform_sender_t, _Domain, _Sender, _Env...>;
+
+inline constexpr __domain::__transform_env transform_env{};
+
+struct _CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_
+{};
+
+template <sender_expr _Sender, class _Env>
+ requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
+decltype(auto) dependent_domain::transform_sender(_Sender&& __sndr,
+ const _Env& __env) const
+{
+ // apply any algorithm-specific transformation to the environment
+ const auto& __env2 = transform_env(*this, (_Sender&&)__sndr, __env);
+
+ // recursively transform the sender to determine the domain
+ return __sexpr_apply((_Sender&&)__sndr,
+ [&]<class _Tag, class _Data, class... _Childs>(
+ _Tag, _Data&& __data, _Childs&&... __childs) {
+ // TODO: propagate meta-exceptions here:
+ auto __sndr2 = __make_sexpr<_Tag>(
+ (_Data&&)__data, __domain::__transform_sender()(
+ *this, (_Childs&&)__childs, __env2)...);
+ using _Sender2 = decltype(__sndr2);
+
+ auto __domain2 = __sexpr_apply(__sndr2, __domain::__common_domain_fn());
+ using _Domain2 = decltype(__domain2);
+
+ if constexpr (same_as<_Domain2, __none_such>)
+ {
+ return __mexception<_CHILD_SENDERS_WITH_DIFFERENT_DOMAINS_,
+ _WITH_SENDER_<_Sender2>>();
+ }
+ else
+ {
+ return __domain::__transform_sender()(__domain2, std::move(__sndr2),
+ __env);
+ }
+ STDEXEC_UNREACHABLE();
+ });
+}
+
+// A helper for use when building sender trees where each node must be
+// transformed.
+template <class _Domain, class _Env>
+auto __make_transformer(_Domain, const _Env& __env)
+{
+ return [&]<class _Tag>(_Tag) {
+ return [&]<class... _Args>(_Args&&... __args) -> decltype(auto) {
+ return stdexec::transform_sender(_Domain(),
+ _Tag()((_Args&&)__args...), __env);
+ };
+ };
+}
+
+/////////////////////////////////////////////////////////////////////////////
+template <class _Tag, class _Domain, class _Sender, class... _Args>
+concept __has_implementation_for =
+ __domain::__has_apply_sender<_Domain, _Tag, _Sender, _Args...> ||
+ __domain::__has_apply_sender<default_domain, _Tag, _Sender, _Args...>;
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.apply_sender]
+inline constexpr struct apply_sender_t
+{
+ template <class _Domain, class _Tag, class _Sender, class... _Args>
+ requires __has_implementation_for<_Tag, _Domain, _Sender, _Args...>
+ STDEXEC_ATTRIBUTE((always_inline))
+ /*constexpr*/ decltype(auto)
+ operator()(_Domain __dom, _Tag, _Sender && __sndr,
+ _Args&&... __args) const
+ {
+ if constexpr (__domain::__has_apply_sender<_Domain, _Tag, _Sender,
+ _Args...>)
+ {
+ return __dom.apply_sender(_Tag(), (_Sender&&)__sndr,
+ (_Args&&)__args...);
+ }
+ else
+ {
+ return default_domain().apply_sender(_Tag(), (_Sender&&)__sndr,
+ (_Args&&)__args...);
+ }
+ }
+} apply_sender{};
+
+template <class _Domain, class _Tag, class _Sender, class... _Args>
+using apply_sender_result_t =
+ __call_result_t<apply_sender_t, _Domain, _Tag, _Sender, _Args...>;
+
+/////////////////////////////////////////////////////////////////////////////
// [execution.sndtraits]
namespace __get_completion_signatures
{
template <class _Sender, class _Env>
-concept __r7_style_sender = same_as<_Env, no_env> &&
- enable_sender<__decay_t<_Sender>>;
+using __tfx_sender =
+ transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>;
template <class _Sender, class _Env>
-concept __with_tag_invoke =
- __valid<tag_invoke_result_t, get_completion_signatures_t, _Sender, _Env>;
+concept __with_tag_invoke = //
+ tag_invocable<get_completion_signatures_t, __tfx_sender<_Sender, _Env>,
+ _Env>;
-template <class _Sender, class...>
-using __member_alias_t = typename __decay_t<_Sender>::completion_signatures;
+template <class _Sender, class _Env>
+using __member_alias_t = //
+ typename __decay_t<__tfx_sender<_Sender, _Env>>::completion_signatures;
-template <class _Sender>
-concept __with_member_alias = __valid<__member_alias_t, _Sender>;
+template <class _Sender, class _Env = empty_env>
+concept __with_member_alias = __mvalid<__member_alias_t, _Sender, _Env>;
struct get_completion_signatures_t
{
template <class _Sender, class _Env>
static auto __impl()
{
- static_assert(STDEXEC_LEGACY_R5_CONCEPTS() || !same_as<_Env, no_env>);
static_assert(sizeof(_Sender),
"Incomplete type used with get_completion_signatures");
static_assert(sizeof(_Env),
"Incomplete type used with get_completion_signatures");
+
if constexpr (__with_tag_invoke<_Sender, _Env>)
{
- using _Result =
- tag_invoke_result_t<get_completion_signatures_t, _Sender, _Env>;
- if constexpr (same_as<_Env, no_env> && __merror<_Result>)
- {
- return (dependent_completion_signatures<no_env>(*)()) nullptr;
- }
- else
- {
- return (_Result(*)()) nullptr;
- }
+ using _TfxSender = __tfx_sender<_Sender, _Env>;
+ using _Result = tag_invoke_result_t<get_completion_signatures_t,
+ _TfxSender, _Env>;
+ return (_Result(*)()) nullptr;
}
- else if constexpr (__with_member_alias<_Sender>)
+ else if constexpr (__with_member_alias<_Sender, _Env>)
{
- return (__member_alias_t<_Sender, _Env>(*)()) nullptr;
+ using _Result = __member_alias_t<_Sender, _Env>;
+ return (_Result(*)()) nullptr;
}
else if constexpr (__awaitable<_Sender, __env_promise<_Env>>)
{
using _Result = __await_result_t<_Sender, __env_promise<_Env>>;
- if constexpr (same_as<_Result,
- dependent_completion_signatures<no_env>>)
- {
- return (dependent_completion_signatures<no_env>(*)()) nullptr;
- }
- else
- {
- return (completion_signatures<
- // set_value_t() or set_value_t(T)
- __minvoke<__remove<void, __qf<set_value_t>>, _Result>,
- set_error_t(std::exception_ptr),
- set_stopped_t()>(*)()) nullptr;
- }
+ return (completion_signatures<
+ // set_value_t() or set_value_t(T)
+ __minvoke<__remove<void, __qf<set_value_t>>, _Result>,
+ set_error_t(std::exception_ptr),
+ set_stopped_t()>(*)()) nullptr;
}
- else
-#if STDEXEC_LEGACY_R5_CONCEPTS()
- if constexpr (__r7_style_sender<_Sender, _Env>)
- {
- return (dependent_completion_signatures<no_env>(*)()) nullptr;
- }
- else
-#endif
- if constexpr (__is_debug_env<_Env>)
+ else if constexpr (__is_debug_env<_Env>)
{
using __tag_invoke::tag_invoke;
// This ought to cause a hard error that indicates where the problem
@@ -1228,18 +1776,16 @@
}
else
{
- return (void (*)()) nullptr;
+ using _Result =
+ __mexception<_UNRECOGNIZED_SENDER_TYPE_<>,
+ _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
+ return (_Result(*)()) nullptr;
}
}
+ // NOT TO SPEC: if we're unable to compute the completion signatures,
+ // return an error type instead of SFINAE.
template <class _Sender, class _Env = __default_env>
- requires(__with_tag_invoke<_Sender, _Env> || //
- __with_member_alias<_Sender> || //
- __awaitable<_Sender, __env_promise<_Env>> || //
-#if STDEXEC_LEGACY_R5_CONCEPTS() //
- __r7_style_sender<_Sender, _Env> || //
-#endif //
- __is_debug_env<_Env>) //
constexpr auto operator()(_Sender&&, const _Env&) const noexcept
-> decltype(__impl<_Sender, _Env>()())
{
@@ -1253,23 +1799,23 @@
/////////////////////////////////////////////////////////////////////////////
// [execution.senders]
+namespace __detail
+{
template <class _Sender>
concept __enable_sender = //
requires { typename _Sender::is_sender; } || //
__awaitable<_Sender, __env_promise<empty_env>>;
+} // namespace __detail
template <class _Sender>
-inline constexpr bool enable_sender = __enable_sender<_Sender>;
+inline constexpr bool enable_sender = __detail::__enable_sender<_Sender>;
-// NOT TO SPEC (YET)
-#if !STDEXEC_LEGACY_R5_CONCEPTS()
-// Here is the R7 sender concepts, not yet enabled.
-template <class _Sender>
-concept sender = //
- enable_sender<__decay_t<_Sender>> && //
- environment_provider<__cref_t<_Sender>> && //
- move_constructible<__decay_t<_Sender>> && //
- constructible_from<__decay_t<_Sender>, _Sender>;
+template <class _Sender, class _Env = empty_env>
+concept sender = enable_sender<__decay_t<_Sender>> && //
+ environment_provider<__cref_t<_Sender>> && //
+ __detail::__consistent_completion_domains<_Sender> && //
+ move_constructible<__decay_t<_Sender>> && //
+ constructible_from<__decay_t<_Sender>, _Sender>;
template <class _Sender, class _Env = empty_env>
concept sender_in = //
@@ -1277,40 +1823,10 @@
requires(_Sender&& __sndr, _Env&& __env) {
{
get_completion_signatures((_Sender&&)__sndr, (_Env&&)__env)
- } -> __valid_completion_signatures<_Env>;
+ } -> __valid_completion_signatures;
};
-template <class _Sender, class _Env = empty_env>
- requires sender_in<_Sender, _Env>
-using completion_signatures_of_t = __completion_signatures_of_t<_Sender, _Env>;
-
-#else
-
-template <class _Sender, class _Env = no_env>
-concept __sender = //
- requires(_Sender&& __sndr, _Env&& __env) {
- get_completion_signatures((_Sender&&)__sndr, (_Env&&)__env);
- } && //
- __valid_completion_signatures<__completion_signatures_of_t<_Sender, _Env>,
- _Env>;
-
-template <class _Sender, class _Env = no_env>
-concept sender =
- // NOT TO SPEC
- // The sender related concepts are temporarily "in flight" being
- // upgraded from P2300R5 to the get_env / enable_sender aware version
- // in P2300R7.
- __sender<_Sender> && //
- __sender<_Sender, _Env> && //
- environment_provider<__cref_t<_Sender>> && //
- move_constructible<__decay_t<_Sender>> && //
- constructible_from<__decay_t<_Sender>, _Sender>;
-
-template <class _Sender, class _Env = empty_env>
-concept sender_in = //
- __sender<_Sender, _Env> && //
- sender<_Sender, _Env>;
-
+#if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
// __checked_completion_signatures is for catching logic bugs in a typed
// sender's metadata. If sender<S> and sender_in<S, Ctx> are both true, then
// they had better report the same metadata. This completion signatures wrapper
@@ -1319,19 +1835,20 @@
auto __checked_completion_signatures(_Sender&& __sndr,
const _Env& __env) noexcept
{
- using _WithEnv = __completion_signatures_of_t<_Sender, _Env>;
- using _WithoutEnv = __completion_signatures_of_t<_Sender, no_env>;
- static_assert(__one_of<_WithoutEnv, _WithEnv,
- dependent_completion_signatures<no_env>>);
- stdexec::__debug_sender<_WithEnv>((_Sender&&)__sndr, __env);
- return _WithEnv{};
+ using __completions_t = __completion_signatures_of_t<_Sender, _Env>;
+ stdexec::__debug_sender<__completions_t>((_Sender&&)__sndr, __env);
+ return __completions_t{};
}
-template <class _Sender, class _Env = no_env>
+template <class _Sender, class _Env = empty_env>
requires sender_in<_Sender, _Env>
using completion_signatures_of_t =
decltype(stdexec::__checked_completion_signatures(__declval<_Sender>(),
__declval<_Env>()));
+#else
+template <class _Sender, class _Env = empty_env>
+ requires sender_in<_Sender, _Env>
+using completion_signatures_of_t = __completion_signatures_of_t<_Sender, _Env>;
#endif
struct __not_a_variant
@@ -1440,11 +1957,11 @@
using __count_of = __msuccess_or_t<__try_count_of<_Tag, _Sender, _Env>>;
template <class _Tag, class _Sender, class _Env = __default_env>
- requires __valid<__count_of, _Tag, _Sender, _Env>
+ requires __mvalid<__count_of, _Tag, _Sender, _Env>
inline constexpr bool __sends = (__v<__count_of<_Tag, _Sender, _Env>> != 0);
template <class _Sender, class _Env = __default_env>
- requires __valid<__count_of, set_stopped_t, _Sender, _Env>
+ requires __mvalid<__count_of, set_stopped_t, _Sender, _Env>
inline constexpr bool sends_stopped = __sends<set_stopped_t, _Sender, _Env>;
template <class _Sender, class _Env = __default_env>
@@ -1456,13 +1973,14 @@
value_types_of_t<_Sender, _Env, __types, __msingle>;
template <class _Sender, class _Env = __default_env>
-concept __single_typed_sender = sender_in<_Sender, _Env> &&
- __valid<__single_sender_value_t, _Sender, _Env>;
+concept __single_typed_sender =
+ sender_in<_Sender, _Env> &&
+ __mvalid<__single_sender_value_t, _Sender, _Env>;
template <class _Sender, class _Env = __default_env>
concept __single_value_variant_sender =
sender_in<_Sender, _Env> &&
- __valid<__single_value_variant_sender_t, _Sender, _Env>;
+ __mvalid<__single_value_variant_sender_t, _Sender, _Env>;
template <class... Errs>
using __nofail = __mbool<sizeof...(Errs) == 0>;
@@ -1480,7 +1998,7 @@
template <class _Error>
using __default_set_error = completion_signatures<set_error_t(_Error)>;
-template <__is_completion_signatures... _Sigs>
+template <__valid_completion_signatures... _Sigs>
using __ensure_concat_ =
__minvoke<__mconcat<__q<completion_signatures>>, _Sigs...>;
@@ -1501,8 +2019,8 @@
template <class _Sender, class _Env, class _Sigs, class _SetVal, class _SetErr,
class _SetStp>
- requires __valid<__compl_sigs_impl, _Sender, _Env, _Sigs, _SetVal, _SetErr,
- _SetStp>
+ requires __mvalid<__compl_sigs_impl, _Sender, _Env, _Sigs, _SetVal, _SetErr,
+ _SetStp>
extern __compl_sigs_impl<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>
__compl_sigs_v;
@@ -1511,25 +2029,6 @@
using __compl_sigs_t =
decltype(__compl_sigs_v<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>);
-template <bool>
-struct __make_compl_sigs
-{
- template <class _Sender, class _Env, class _Sigs, class _SetVal,
- class _SetErr, class _SetStp>
- using __f = __compl_sigs_t<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>;
-};
-
-template <>
-struct __make_compl_sigs<true>
-{
- template <class _Sender, class _Env, class _Sigs, class _SetVal,
- class _SetErr, class _SetStp>
- using __f = //
- __msuccess_or_t<
- __compl_sigs_t<_Sender, _Env, _Sigs, _SetVal, _SetErr, _SetStp>,
- dependent_completion_signatures<_Env>>;
-};
-
template < //
class _Sender, //
class _Env = __default_env, //
@@ -1538,8 +2037,8 @@
class _SetError = __q<__default_set_error>, //
class _SetStopped = completion_signatures<set_stopped_t()>> //
using __try_make_completion_signatures = //
- __minvoke<__make_compl_sigs<same_as<_Env, no_env>>, _Sender, _Env, _Sigs,
- _SetValue, _SetError, _SetStopped>;
+ __meval<__compl_sigs_t, _Sender, _Env, _Sigs, _SetValue, _SetError,
+ _SetStopped>;
} // namespace __compl_sigs
using __compl_sigs::__try_make_completion_signatures;
@@ -1612,10 +2111,10 @@
template < //
class _Sender, //
class _Env = __default_env, //
- __valid_completion_signatures<_Env> _Sigs = completion_signatures<>, //
+ __valid_completion_signatures _Sigs = completion_signatures<>, //
template <class...> class _SetValue = __compl_sigs::__default_set_value, //
template <class> class _SetError = __compl_sigs::__default_set_error, //
- __valid_completion_signatures<_Env> _SetStopped =
+ __valid_completion_signatures _SetStopped =
completion_signatures<set_stopped_t()>>
requires sender_in<_Sender, _Env>
using make_completion_signatures = //
@@ -1635,8 +2134,7 @@
{
template <class _Scheduler>
requires tag_invocable<schedule_t, _Scheduler>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- auto
+ STDEXEC_ATTRIBUTE((host, device)) auto
operator()(_Scheduler&& __sched) const
noexcept(nothrow_tag_invocable<schedule_t, _Scheduler>)
{
@@ -1677,7 +2175,7 @@
template <class _Scheduler>
concept __sender_has_completion_scheduler =
requires(_Scheduler&& __sched,
- const get_completion_scheduler_t<set_value_t>&& __tag) {
+ get_completion_scheduler_t<set_value_t>&& __tag) {
{
tag_invoke(std::move(__tag),
get_env(schedule((_Scheduler&&)__sched)))
@@ -1714,7 +2212,9 @@
{
template <class _Op>
requires tag_invocable<start_t, _Op&>
- void operator()(_Op& __op) const noexcept
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ void
+ operator()(_Op& __op) const noexcept
{
static_assert(nothrow_tag_invocable<start_t, _Op&>);
(void)tag_invoke(start_t{}, __op);
@@ -1778,7 +2278,21 @@
~__operation_base()
{
if (__coro_)
+ {
+#if STDEXEC_MSVC()
+ // MSVCBUG
+ // https://developercommunity.visualstudio.com/t/Double-destroy-of-a-local-in-coroutine-d/10456428
+
+ // Reassign __coro_ before calling destroy to make the mutation
+ // observable and to hopefully ensure that the compiler does not
+ // eliminate it.
+ auto __coro = __coro_;
+ __coro_ = {};
+ __coro.destroy();
+#else
__coro_.destroy();
+#endif
+ }
}
friend void tag_invoke(start_t, __operation_base& __self) noexcept
@@ -1827,18 +2341,18 @@
}
template <class _Awaitable>
- _Awaitable&& await_transform(_Awaitable&& __await) noexcept
+ _Awaitable&& await_transform(_Awaitable&& __awaitable) noexcept
{
- return (_Awaitable&&)__await;
+ return (_Awaitable&&)__awaitable;
}
template <class _Awaitable>
requires tag_invocable<as_awaitable_t, _Awaitable, __t&>
- auto await_transform(_Awaitable&& __await) //
+ auto await_transform(_Awaitable&& __awaitable) //
noexcept(nothrow_tag_invocable<as_awaitable_t, _Awaitable, __t&>)
-> tag_invoke_result_t<as_awaitable_t, _Awaitable, __t&>
{
- return tag_invoke(as_awaitable, (_Awaitable&&)__await, *this);
+ return tag_invoke(as_awaitable, (_Awaitable&&)__awaitable, *this);
}
// Pass through the get_env receiver query
@@ -1890,19 +2404,22 @@
}
template <class _Awaitable, class _Receiver>
- static __operation_t<_Receiver> __co_impl(_Awaitable __await,
- _Receiver __rcvr)
+#if STDEXEC_GCC() && (__GNUC__ > 11)
+ __attribute__((__used__))
+#endif
+ static __operation_t<_Receiver>
+ __co_impl(_Awaitable __awaitable, _Receiver __rcvr)
{
using __result_t = __await_result_t<_Awaitable, __promise_t<_Receiver>>;
std::exception_ptr __eptr;
try
{
if constexpr (same_as<__result_t, void>)
- co_await (co_await (_Awaitable&&) __await,
+ co_await (co_await (_Awaitable&&) __awaitable,
__co_call(set_value, (_Receiver&&)__rcvr));
else
co_await __co_call(set_value, (_Receiver&&)__rcvr,
- co_await (_Awaitable&&) __await);
+ co_await (_Awaitable&&) __awaitable);
}
catch (...)
{
@@ -1923,10 +2440,10 @@
public:
template <class _Receiver, __awaitable<__promise_t<_Receiver>> _Awaitable>
requires receiver_of<_Receiver, __completions_t<_Receiver, _Awaitable>>
- __operation_t<_Receiver> operator()(_Awaitable&& __await,
+ __operation_t<_Receiver> operator()(_Awaitable&& __awaitable,
_Receiver __rcvr) const
{
- return __co_impl((_Awaitable&&)__await, (_Receiver&&)__rcvr);
+ return __co_impl((_Awaitable&&)__awaitable, (_Receiver&&)__rcvr);
}
};
} // namespace __connect_awaitable_
@@ -1944,54 +2461,81 @@
{
struct connect_t;
-template <class _Tp>
-STDEXEC_R5_SENDER_DEPRECATION_WARNING //
- void
- _PLEASE_UPDATE_YOUR_SENDER_TYPE()
-{}
-
-template <class _Tp>
-STDEXEC_R5_RECEIVER_DEPRECATION_WARNING //
- void
- _PLEASE_UPDATE_YOUR_RECEIVER_TYPE()
-{}
+template <class _Sender, class _Receiver>
+using __tfx_sender = //
+ transform_sender_result_t<__late_domain_of_t<_Sender, env_of_t<_Receiver&>>,
+ _Sender, env_of_t<_Receiver&>>;
template <class _Sender, class _Receiver>
-concept __connectable_with_tag_invoke =
+concept __connectable_with_tag_invoke_ = //
receiver<_Receiver> && //
sender_in<_Sender, env_of_t<_Receiver>> && //
__receiver_from<_Receiver, _Sender> && //
tag_invocable<connect_t, _Sender, _Receiver>;
+template <class _Sender, class _Receiver>
+concept __connectable_with_tag_invoke = //
+ __connectable_with_tag_invoke_<__tfx_sender<_Sender, _Receiver>, _Receiver>;
+
+template <class _Sender, class _Receiver>
+concept __connectable_with_co_await = //
+ __callable<__connect_awaitable_t, __tfx_sender<_Sender, _Receiver>,
+ _Receiver>;
+
struct connect_t
{
+ template <class _Sender, class _Env>
+ static constexpr bool __check_signatures()
+ {
+ if constexpr (sender_in<_Sender, _Env>)
+ {
+ // Instantiate __debug_sender via completion_signatures_of_t
+ // to check that the actual completions match the expected
+ // completions.
+ //
+ // Instantiate completion_signatures_of_t only if sender_in
+ // is true to workaround Clang not implementing CWG#2369 yet
+ // (connect() does have a constraint for _Sender satisfying
+ // sender_in).
+ using __checked_signatures
+ [[maybe_unused]] = completion_signatures_of_t<_Sender, _Env>;
+ }
+ return true;
+ }
+
template <class _Sender, class _Receiver>
static constexpr auto __select_impl() noexcept
{
- // Report that 2300R5-style senders and receivers are deprecated:
- if constexpr (!enable_sender<__decay_t<_Sender>>)
- _PLEASE_UPDATE_YOUR_SENDER_TYPE<__decay_t<_Sender>>();
+#if STDEXEC_ENABLE_EXTRA_TYPE_CHECKING()
+ static_assert(__check_signatures<_Sender, env_of_t<_Receiver>>());
+#endif
- if constexpr (!enable_receiver<__decay_t<_Receiver>>)
- _PLEASE_UPDATE_YOUR_RECEIVER_TYPE<__decay_t<_Receiver>>();
+ using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver&>>;
+ constexpr bool _NothrowTfxSender =
+ __nothrow_callable<get_env_t, _Receiver&> &&
+ __nothrow_callable<transform_sender_t, _Domain, _Sender,
+ env_of_t<_Receiver&>>;
+ using _TfxSender = __tfx_sender<_Sender, _Receiver&>;
if constexpr (__connectable_with_tag_invoke<_Sender, _Receiver>)
{
- using _Result = tag_invoke_result_t<connect_t, _Sender, _Receiver>;
- constexpr bool _Nothrow =
- nothrow_tag_invocable<connect_t, _Sender, _Receiver>;
+ using _Result =
+ tag_invoke_result_t<connect_t, _TfxSender, _Receiver>;
+ constexpr bool _Nothrow = //
+ _NothrowTfxSender &&
+ nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>;
return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
}
- else if constexpr (__callable<__connect_awaitable_t, _Sender,
- _Receiver>)
+ else if constexpr (__connectable_with_co_await<_Sender, _Receiver>)
{
using _Result =
- __call_result_t<__connect_awaitable_t, _Sender, _Receiver>;
+ __call_result_t<__connect_awaitable_t, _TfxSender, _Receiver>;
return static_cast<_Result (*)()>(nullptr);
}
else
{
- return static_cast<__debug::__debug_operation (*)() noexcept>(
+ using _Result = __debug::__debug_operation;
+ return static_cast<_Result (*)() noexcept(_NothrowTfxSender)>(
nullptr);
}
}
@@ -2001,33 +2545,42 @@
template <sender _Sender, receiver _Receiver>
requires __connectable_with_tag_invoke<_Sender, _Receiver> ||
- __callable<__connect_awaitable_t, _Sender, _Receiver> ||
+ __connectable_with_co_await<_Sender, _Receiver> ||
__is_debug_env<env_of_t<_Receiver>>
auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const
noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>)
-> __call_result_t<__select_impl_t<_Sender, _Receiver>>
{
+ using _TfxSender = __tfx_sender<_Sender, _Receiver&>;
+ auto&& __env = get_env(__rcvr);
+ auto __domain = __get_late_domain(__sndr, __env);
+
if constexpr (__connectable_with_tag_invoke<_Sender, _Receiver>)
{
static_assert(
operation_state<
- tag_invoke_result_t<connect_t, _Sender, _Receiver>>,
+ tag_invoke_result_t<connect_t, _TfxSender, _Receiver>>,
"stdexec::connect(sender, receiver) must return a type that "
"satisfies the operation_state concept");
- return tag_invoke(connect_t{}, (_Sender&&)__sndr,
- (_Receiver&&)__rcvr);
+ return tag_invoke(
+ connect_t{},
+ transform_sender(__domain, (_Sender&&)__sndr, __env),
+ (_Receiver&&)__rcvr);
}
- else if constexpr (__callable<__connect_awaitable_t, _Sender,
- _Receiver>)
+ else if constexpr (__connectable_with_co_await<_Sender, _Receiver>)
{
- return __connect_awaitable((_Sender&&)__sndr, (_Receiver&&)__rcvr);
+ return __connect_awaitable( //
+ transform_sender(__domain, (_Sender&&)__sndr, __env),
+ (_Receiver&&)__rcvr);
}
else
{
- // This should generate an instantiate backtrace that contains
+ // This should generate an instantiation backtrace that contains
// useful debugging information.
using __tag_invoke::tag_invoke;
- tag_invoke(*this, (_Sender&&)__sndr, (_Receiver&&)__rcvr);
+ tag_invoke(*this,
+ transform_sender(__domain, (_Sender&&)__sndr, __env),
+ (_Receiver&&)__rcvr);
}
}
@@ -2044,15 +2597,12 @@
/////////////////////////////////////////////////////////////////////////////
// [exec.snd]
template <class _Sender, class _Receiver>
-concept sender_to =
- receiver<_Receiver> && //
- __sender<_Sender,
- env_of_t<_Receiver>> && // NOT TO SPEC: for simpler diagnostics
- sender_in<_Sender, env_of_t<_Receiver>> && //
- __receiver_from<_Receiver, _Sender> && //
- requires(_Sender&& __sndr, _Receiver&& __rcvr) {
- connect((_Sender&&)__sndr, (_Receiver&&)__rcvr);
- };
+concept sender_to = receiver<_Receiver> && //
+ sender_in<_Sender, env_of_t<_Receiver>> && //
+ __receiver_from<_Receiver, _Sender> && //
+ requires(_Sender&& __sndr, _Receiver&& __rcvr) {
+ connect((_Sender&&)__sndr, (_Receiver&&)__rcvr);
+ };
template <class _Tag, class... _Args>
_Tag __tag_of_sig_(_Tag (*)(_Args...));
@@ -2066,12 +2616,6 @@
__tag_of_sig_t<_SetSig>, _Sender, _Env,
__qf<__tag_of_sig_t<_SetSig>>, __q<__types>>>;
-template <class _Fun, class _CPO, class _Sender, class... _As>
-concept __tag_invocable_with_completion_scheduler =
- __has_completion_scheduler<_Sender, _CPO> &&
- tag_invocable<_Fun, __completion_scheduler_for<_Sender, _CPO>, _Sender,
- _As...>;
-
#if !STDEXEC_STD_NO_COROUTINES_
/////////////////////////////////////////////////////////////////////////////
// stdexec::as_awaitable [execution.coro_utils.as_awaitable]
@@ -2159,7 +2703,8 @@
template <class _Sender, class _Promise>
using __value_t =
- __decay_t<__single_sender_value_t<_Sender, env_of_t<_Promise&>>>;
+ __decay_t<__value_types_of_t<_Sender, env_of_t<_Promise&>,
+ __msingle_or<void>, __msingle_or<void>>>;
template <class _Sender, class _Promise>
using __receiver_t =
@@ -2227,7 +2772,8 @@
template <class _Sender, class _Promise>
concept __awaitable_sender =
- __single_typed_sender<_Sender, env_of_t<_Promise&>> && //
+ sender_in<_Sender, env_of_t<_Promise&>> && //
+ __mvalid<__value_t, _Sender, _Promise> && //
sender_to<_Sender, __receiver_t<_Sender, _Promise>> && //
requires(_Promise& __promise) {
{
@@ -2432,75 +2978,83 @@
using __with_awaitable_senders::with_awaitable_senders;
#endif
+namespace
+{
+inline constexpr auto __ref = []<class _Ty>(_Ty& __ty) noexcept {
+ return [__ty = &__ty]() noexcept -> decltype(auto) { return (*__ty); };
+};
+}
+
+template <class _Ty>
+using __ref_t = decltype(__ref(__declval<_Ty&>()));
+
/////////////////////////////////////////////////////////////////////////////
// NOT TO SPEC: __submit
namespace __submit_
{
-template <class _ReceiverId>
-struct __operation_base;
-
-template <class _ReceiverId>
-struct __operation_base
-{
- using _Receiver = __t<_ReceiverId>;
- _Receiver __rcvr_;
-
- using __delete_fn_t = void(__operation_base<_ReceiverId>*) noexcept;
- __delete_fn_t* __delete_;
-};
-
-template <class _ReceiverId>
+template <class _OpRef>
struct __receiver
{
- using _Receiver = stdexec::__t<_ReceiverId>;
+ using is_receiver = void;
+ using __t = __receiver;
+ using __id = __receiver;
- struct __t
+ using _Operation = __decay_t<__call_result_t<_OpRef>>;
+ using _Receiver = stdexec::__t<__mapply<__q<__msecond>, _Operation>>;
+
+ _OpRef __opref_;
+
+ // Forward all the receiver ops, and delete the operation state.
+ template <__completion_tag _Tag, class... _As>
+ requires __callable<_Tag, _Receiver, _As...>
+ friend void tag_invoke(_Tag __tag, __receiver&& __self,
+ _As&&... __as) noexcept
{
- using is_receiver = void;
- using __id = __receiver;
- __operation_base<_ReceiverId>* __op_state_;
+ __tag((_Receiver&&)__self.__opref_().__rcvr_, (_As&&)__as...);
+ __self.__delete_op();
+ }
- // Forward all the receiver ops, and delete the operation state.
- template <__completion_tag _Tag, class... _As>
- requires __callable<_Tag, _Receiver, _As...>
- friend void tag_invoke(
- _Tag __tag, __t&& __self,
- _As&&... __as) noexcept(__nothrow_callable<_Tag, _Receiver, _As...>)
+ void __delete_op() noexcept
+ {
+ _Operation* __op = &__opref_();
+ if constexpr (__callable<get_allocator_t, env_of_t<_Receiver>>)
{
- // Delete the state as cleanup:
- auto __g = __scope_guard{__self.__op_state_->__delete_,
- __self.__op_state_};
- return __tag((_Receiver&&)__self.__op_state_->__rcvr_,
- (_As&&)__as...);
+ auto&& __env = get_env(__op->__rcvr_);
+ auto __alloc = get_allocator(__env);
+ using _Alloc = decltype(__alloc);
+ using _OpAlloc = typename std::allocator_traits<
+ _Alloc>::template rebind_alloc<_Operation>;
+ _OpAlloc __op_alloc{__alloc};
+ std::allocator_traits<_OpAlloc>::destroy(__op_alloc, __op);
+ std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op, 1);
}
+ else
+ {
+ delete __op;
+ }
+ }
- // Forward all receiever queries.
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<_Receiver>
- {
- return get_env(__self.__op_state_->__rcvr_);
- }
- };
+ // Forward all receiever queries.
+ friend auto tag_invoke(get_env_t, const __receiver& __self) noexcept
+ -> env_of_t<_Receiver&>
+ {
+ return get_env(__self.__opref_().__rcvr_);
+ }
};
-template <class _ReceiverId>
-using __receiver_t = __t<__receiver<_ReceiverId>>;
template <class _SenderId, class _ReceiverId>
-struct __operation : __operation_base<_ReceiverId>
+struct __operation
{
using _Sender = stdexec::__t<_SenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
+ using __receiver_t = __receiver<__ref_t<__operation>>;
- connect_result_t<_Sender, __receiver_t<_ReceiverId>> __op_state_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_;
+ connect_result_t<_Sender, __receiver_t> __op_state_;
- template <__decays_to<_Receiver> _CvrefReceiver>
- __operation(_Sender&& __sndr, _CvrefReceiver&& __rcvr) :
- __operation_base<_ReceiverId>{
- (_CvrefReceiver&&)__rcvr,
- [](__operation_base<_ReceiverId>* __self) noexcept {
- delete static_cast<__operation*>(__self);
- }},
- __op_state_(connect((_Sender&&)__sndr, __receiver_t<_ReceiverId>{this}))
+ __operation(_Sender&& __sndr, _Receiver __rcvr) :
+ __rcvr_((_Receiver&&)__rcvr),
+ __op_state_(connect((_Sender&&)__sndr, __receiver_t{__ref(*this)}))
{}
};
@@ -2509,9 +3063,36 @@
template <receiver _Receiver, sender_to<_Receiver> _Sender>
void operator()(_Sender&& __sndr, _Receiver __rcvr) const noexcept(false)
{
- start((new __operation<__id<_Sender>, __id<_Receiver>>{
- (_Sender&&)__sndr, (_Receiver&&)__rcvr})
- ->__op_state_);
+ if constexpr (__callable<get_allocator_t, env_of_t<_Receiver>>)
+ {
+ auto&& __env = get_env(__rcvr);
+ auto __alloc = get_allocator(__env);
+ using _Alloc = decltype(__alloc);
+ using _Op = __operation<__id<_Sender>, __id<_Receiver>>;
+ using _OpAlloc = typename std::allocator_traits<
+ _Alloc>::template rebind_alloc<_Op>;
+ _OpAlloc __op_alloc{__alloc};
+ auto __op = std::allocator_traits<_OpAlloc>::allocate(__op_alloc,
+ 1);
+ try
+ {
+ std::allocator_traits<_OpAlloc>::construct(
+ __op_alloc, __op, (_Sender&&)__sndr, (_Receiver&&)__rcvr);
+ start(__op->__op_state_);
+ }
+ catch (...)
+ {
+ std::allocator_traits<_OpAlloc>::deallocate(__op_alloc, __op,
+ 1);
+ throw;
+ }
+ }
+ else
+ {
+ start((new __operation<__id<_Sender>, __id<_Receiver>>{
+ (_Sender&&)__sndr, (_Receiver&&)__rcvr})
+ ->__op_state_);
+ }
}
};
} // namespace __submit_
@@ -2521,52 +3102,61 @@
namespace __inln
{
+struct __scheduler;
+
+template <class _Receiver>
+struct __op : __immovable
+{
+ _Receiver __recv_;
+
+ friend void tag_invoke(start_t, __op& __self) noexcept
+ {
+ set_value((_Receiver&&)__self.__recv_);
+ }
+};
+
+struct __schedule_t
+{
+ static auto get_env(__ignore) noexcept
+ -> __env::__prop<__scheduler(get_completion_scheduler_t<set_value_t>)>;
+
+ using __compl_sigs = stdexec::completion_signatures<set_value_t()>;
+ static __compl_sigs get_completion_signatures(__ignore, __ignore);
+
+ template <receiver_of<__compl_sigs> _Receiver>
+ static auto
+ connect(__ignore,
+ _Receiver __rcvr) noexcept(__nothrow_decay_copyable<_Receiver>)
+ {
+ return __op<_Receiver>{{}, (_Receiver&&)__rcvr};
+ }
+};
+
struct __scheduler
{
using __t = __scheduler;
using __id = __scheduler;
- template <class _Receiver>
- struct __op : __immovable
+ STDEXEC_ATTRIBUTE((host, device))
+ friend auto tag_invoke(schedule_t, __scheduler)
{
- _Receiver __recv_;
+ return __make_sexpr<__schedule_t>();
+ }
- friend void tag_invoke(start_t, __op& __self) noexcept
- {
- set_value((_Receiver&&)__self.__recv_);
- }
- };
-
- struct __sender
+ friend forward_progress_guarantee
+ tag_invoke(get_forward_progress_guarantee_t, __scheduler) noexcept
{
- using __t = __sender;
- using __id = __sender;
- using is_sender = void;
- using completion_signatures =
- stdexec::completion_signatures<set_value_t()>;
-
- template <receiver_of<completion_signatures> _Receiver>
- friend __op<_Receiver> tag_invoke(connect_t, __sender, _Receiver __rcvr)
- {
- return {{}, (_Receiver&&)__rcvr};
- }
-
- friend auto tag_invoke(get_env_t, __sender) noexcept
- {
- return __env::__env_fn{
- [](get_completion_scheduler_t<set_value_t>) noexcept {
- return __scheduler{};
- }};
- }
- };
-
- friend __sender tag_invoke(schedule_t, __scheduler)
- {
- return {};
+ return forward_progress_guarantee::weakly_parallel;
}
bool operator==(const __scheduler&) const noexcept = default;
};
+
+inline auto __schedule_t::get_env(__ignore) noexcept
+ -> __env::__prop<__scheduler(get_completion_scheduler_t<set_value_t>)>
+{
+ return __mkprop(__scheduler{}, get_completion_scheduler<set_value_t>);
+}
} // namespace __inln
/////////////////////////////////////////////////////////////////////////////
@@ -2582,7 +3172,7 @@
{
using is_receiver = void;
using __id = __detached_receiver;
- STDEXEC_NO_UNIQUE_ADDRESS _Env __env_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Env __env_;
template <same_as<set_value_t> _Tag, class... _As>
friend void tag_invoke(_Tag, __t&&, _As&&...) noexcept
@@ -2605,52 +3195,43 @@
}
};
};
-template <class _Env>
+template <class _Env = empty_env>
using __detached_receiver_t = __t<__detached_receiver<__id<__decay_t<_Env>>>>;
-struct start_detached_t;
-
-// When looking for user-defined customizations of start_detached, these
-// are the signatures to test against, in order:
-using _Sender = __0;
-using _Env = __1;
-using __cust_sigs = //
- __types<tag_invoke_t(start_detached_t, _Sender),
- tag_invoke_t(start_detached_t, _Sender, _Env),
- tag_invoke_t(start_detached_t, get_scheduler_t(_Env&), _Sender),
- tag_invoke_t(start_detached_t, get_scheduler_t(_Env&), _Sender,
- _Env)>;
-
-template <class _Sender, class _Env>
-inline constexpr bool __is_start_detached_customized =
- __minvocable<__which<__cust_sigs>, _Sender, _Env>;
-
-struct __submit_detached
-{
- template <class _Sender, class _Env>
- void operator()(_Sender&& __sndr, _Env&& __env) const
- {
- __submit((_Sender&&)__sndr, __detached_receiver_t<_Env>{(_Env&&)__env});
- }
-};
-
-template <class _Sender, class _Env>
-using __dispatcher_for =
- __make_dispatcher<__cust_sigs, __mconst<__submit_detached>, _Sender, _Env>;
-
struct start_detached_t
{
- template <sender _Sender, class _Env = empty_env>
- requires sender_to<_Sender, __detached_receiver_t<_Env>> ||
- __is_start_detached_customized<_Sender, _Env>
- void operator()(_Sender&& __sndr, _Env&& __env = _Env{}) const noexcept(
- __nothrow_callable<__dispatcher_for<_Sender, _Env>, _Sender, _Env>)
+ template <sender_in<empty_env> _Sender>
+ requires __callable<apply_sender_t, __early_domain_of_t<_Sender>,
+ start_detached_t, _Sender>
+ void operator()(_Sender&& __sndr) const
{
- using _Dispatcher = __dispatcher_for<_Sender, _Env>;
- // The selected implementation should return void
- static_assert(
- same_as<void, __call_result_t<_Dispatcher, _Sender, _Env>>);
- _Dispatcher{}((_Sender&&)__sndr, (_Env&&)__env);
+ auto __domain = __get_early_domain(__sndr);
+ stdexec::apply_sender(__domain, *this, (_Sender&&)__sndr);
+ }
+
+ template <class _Env, sender_in<_Env> _Sender>
+ requires __callable<apply_sender_t, __late_domain_of_t<_Sender, _Env>,
+ start_detached_t, _Sender, _Env>
+ void operator()(_Sender&& __sndr, _Env&& __env) const
+ {
+ auto __domain = __get_late_domain(__sndr, __env);
+ stdexec::apply_sender(__domain, *this, (_Sender&&)__sndr,
+ (_Env&&)__env);
+ }
+
+ using _Sender = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(start_detached_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(const _Sender&)),
+ _Sender),
+ tag_invoke_t(start_detached_t, _Sender)>;
+
+ template <class _Sender, class _Env = empty_env>
+ requires sender_to<_Sender, __detached_receiver_t<_Env>>
+ void apply_sender(_Sender&& __sndr, _Env&& __env = {}) const
+ {
+ __submit((_Sender&&)__sndr, __detached_receiver_t<_Env>{(_Env&&)__env});
}
};
} // namespace __start_detached
@@ -2662,10 +3243,7 @@
// [execution.senders.factories]
namespace __just
{
-template <class _Tag, class... _Ts>
-using __completion_signatures_ = completion_signatures<_Tag(_Ts...)>;
-
-template <class _ReceiverId, class _Tag, class... _Ts>
+template <class _ReceiverId, class _Tag, class _Tuple>
struct __operation
{
using _Receiver = stdexec::__t<_ReceiverId>;
@@ -2673,126 +3251,108 @@
struct __t : __immovable
{
using __id = __operation;
- std::tuple<_Ts...> __vals_;
+ _Tuple __vals_;
_Receiver __rcvr_;
friend void tag_invoke(start_t, __t& __op_state) noexcept
{
- std::apply(
- [&__op_state](_Ts&... __ts) {
- _Tag{}((_Receiver&&)__op_state.__rcvr_, (_Ts&&)__ts...);
+ using __tag_t = _Tag;
+ using __receiver_t = _Receiver;
+ __apply(
+ [&__op_state]<class... _Ts>(_Ts&... __ts) {
+ __tag_t()((__receiver_t&&)__op_state.__rcvr_, (_Ts&&)__ts...);
},
__op_state.__vals_);
}
};
};
-template <class _Tag, class... _Ts>
-struct __basic_sender
+template <class _SetTag, class _Receiver>
+struct __connect_fn
{
- template <class _Receiver>
- using __operation_t =
- stdexec::__t<__operation<stdexec::__id<_Receiver>, _Tag, _Ts...>>;
+ _Receiver& __rcvr_;
- struct __t
+ template <class _Tuple>
+ auto operator()(__ignore, _Tuple&& __tup) const
+ noexcept(__nothrow_decay_copyable<_Tuple>)
+ -> __t<__operation<__id<_Receiver>, _SetTag, __decay_t<_Tuple>>>
{
- using __id = __basic_sender;
- using is_sender = void;
- using completion_signatures = __completion_signatures_<_Tag, _Ts...>;
-
- std::tuple<_Ts...> __vals_;
-
- template <receiver_of<completion_signatures> _Receiver>
- requires(copy_constructible<_Ts> && ...)
- friend auto tag_invoke(connect_t, const __t& __sndr,
- _Receiver __rcvr) //
- noexcept((std::is_nothrow_copy_constructible_v<_Ts> && ...))
- -> __operation_t<_Receiver>
- {
- return {{}, __sndr.__vals_, (_Receiver&&)__rcvr};
- }
-
- template <receiver_of<completion_signatures> _Receiver>
- friend auto tag_invoke(connect_t, __t&& __sndr, _Receiver __rcvr) //
- noexcept((std::is_nothrow_move_constructible_v<_Ts> && ...))
- -> __operation_t<_Receiver>
- {
- return {{}, ((__t&&)__sndr).__vals_, (_Receiver&&)__rcvr};
- }
-
- friend empty_env tag_invoke(get_env_t, const __t&) noexcept
- {
- return {};
- }
- };
+ return {{}, (_Tuple&&)__tup, (_Receiver&&)__rcvr_};
+ }
};
-template <class... _Values>
-struct __sender
+template <class _JustTag, class _SetTag>
+struct __just_impl
{
- using __base = stdexec::__t<__basic_sender<set_value_t, _Values...>>;
+ template <class _Sender>
+ using __compl_sigs = //
+ completion_signatures< //
+ __mapply< //
+ __qf<_SetTag>, __decay_t<__data_of<_Sender>>>>;
- struct __t : __base
+ template <sender_expr_for<_JustTag> _Sender>
+ static __compl_sigs<_Sender> get_completion_signatures(_Sender&&, __ignore)
{
- using __id = __sender;
- };
-};
+ return {};
+ }
-template <class _Error>
-struct __error_sender
-{
- using __base = stdexec::__t<__basic_sender<set_error_t, _Error>>;
-
- struct __t : __base
+ template <sender_expr_for<_JustTag> _Sender,
+ receiver_of<__compl_sigs<_Sender>> _Receiver>
+ static auto connect(_Sender&& __sndr, _Receiver __rcvr) noexcept(
+ __nothrow_callable<__sexpr_apply_t, _Sender,
+ __connect_fn<_SetTag, _Receiver>>)
+ -> __call_result_t<__sexpr_apply_t, _Sender,
+ __connect_fn<_SetTag, _Receiver>>
{
- using __id = __error_sender;
- };
+ return __sexpr_apply((_Sender&&)__sndr,
+ __connect_fn<_SetTag, _Receiver>{__rcvr});
+ }
+
+ static empty_env get_env(__ignore) noexcept
+ {
+ return {};
+ }
};
-struct __stopped_sender : __t<__basic_sender<set_stopped_t>>
-{
- using __id = __stopped_sender;
- using __t = __stopped_sender;
-};
-
-inline constexpr struct __just_t
+struct just_t : __just_impl<just_t, set_value_t>
{
template <__movable_value... _Ts>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- __t<__sender<__decay_t<_Ts>...>>
- operator()(_Ts&&... __ts) const
- noexcept((__nothrow_constructible_from<__decay_t<_Ts>, _Ts> && ...))
+ STDEXEC_ATTRIBUTE((host, device))
+ auto operator()(_Ts&&... __ts) const
+ noexcept((__nothrow_decay_copyable<_Ts> && ...))
{
- return {{{(_Ts&&)__ts...}}};
+ return __make_sexpr<just_t>(__decayed_tuple<_Ts...>{(_Ts&&)__ts...});
}
-} just{};
+};
-inline constexpr struct __just_error_t
+struct just_error_t : __just_impl<just_error_t, set_error_t>
{
template <__movable_value _Error>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- __t<__error_sender<__decay_t<_Error>>>
- operator()(_Error&& __err) const
- noexcept(__nothrow_constructible_from<__decay_t<_Error>, _Error>)
+ STDEXEC_ATTRIBUTE((host, device))
+ auto operator()(_Error&& __err) const
+ noexcept(__nothrow_decay_copyable<_Error>)
{
- return {{{(_Error&&)__err}}};
+ return __make_sexpr<just_error_t>(
+ __decayed_tuple<_Error>{(_Error&&)__err});
}
-} just_error{};
+};
-inline constexpr struct __just_stopped_t
+struct just_stopped_t : __just_impl<just_stopped_t, set_stopped_t>
{
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- __stopped_sender
- operator()() const noexcept
+ STDEXEC_ATTRIBUTE((host, device)) auto operator()() const noexcept
{
- return {{}};
+ return __make_sexpr<just_stopped_t>(__decayed_tuple<>());
}
-} just_stopped{};
+};
} // namespace __just
-using __just::just;
-using __just::just_error;
-using __just::just_stopped;
+using __just::just_error_t;
+using __just::just_stopped_t;
+using __just::just_t;
+
+inline constexpr just_t just{};
+inline constexpr just_error_t just_error{};
+inline constexpr just_stopped_t just_stopped{};
/////////////////////////////////////////////////////////////////////////////
// [execution.execute]
@@ -2838,21 +3398,27 @@
{
template <scheduler _Scheduler, class _Fun>
requires __callable<_Fun&> && move_constructible<_Fun>
- void operator()(_Scheduler&& __sched, _Fun __fun) const //
- noexcept(noexcept(__submit(schedule((_Scheduler&&)__sched),
- __as_receiver<_Fun>{(_Fun&&)__fun})))
+ void operator()(_Scheduler&& __sched, _Fun __fun) const noexcept(false)
{
- (void)__submit(schedule((_Scheduler&&)__sched),
- __as_receiver<_Fun>{(_Fun&&)__fun});
+ // Look for a legacy customization
+ if constexpr (tag_invocable<execute_t, _Scheduler, _Fun>)
+ {
+ tag_invoke(execute_t{}, (_Scheduler&&)__sched, (_Fun&&)__fun);
+ }
+ else
+ {
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ stdexec::apply_sender(__domain, *this,
+ schedule((_Scheduler&&)__sched),
+ (_Fun&&)__fun);
+ }
}
- template <scheduler _Scheduler, class _Fun>
- requires __callable<_Fun&> && move_constructible<_Fun> &&
- tag_invocable<execute_t, _Scheduler, _Fun>
- void operator()(_Scheduler&& __sched, _Fun __fun) const
- noexcept(nothrow_tag_invocable<execute_t, _Scheduler, _Fun>)
+ template <sender_of<set_value_t()> _Sender, class _Fun>
+ requires __callable<_Fun&> && move_constructible<_Fun>
+ void apply_sender(_Sender&& __sndr, _Fun __fun) const noexcept(false)
{
- (void)tag_invoke(execute_t{}, (_Scheduler&&)__sched, (_Fun&&)__fun);
+ __submit((_Sender&&)__sndr, __as_receiver<_Fun>{(_Fun&&)__fun});
}
};
} // namespace __execute_
@@ -2886,13 +3452,14 @@
template <class _T0, class _T1>
struct __compose : sender_adaptor_closure<__compose<_T0, _T1>>
{
- STDEXEC_NO_UNIQUE_ADDRESS _T0 __t0_;
- STDEXEC_NO_UNIQUE_ADDRESS _T1 __t1_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _T0 __t0_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _T1 __t1_;
template <sender _Sender>
requires __callable<_T0, _Sender> &&
__callable<_T1, __call_result_t<_T0, _Sender>>
- __call_result_t<_T1, __call_result_t<_T0, _Sender>>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ __call_result_t<_T1, __call_result_t<_T0, _Sender>>
operator()(_Sender&& __sndr) &&
{
return ((_T1&&)__t1_)(((_T0&&)__t0_)((_Sender&&)__sndr));
@@ -2901,7 +3468,8 @@
template <sender _Sender>
requires __callable<const _T0&, _Sender> &&
__callable<const _T1&, __call_result_t<const _T0&, _Sender>>
- __call_result_t<_T1, __call_result_t<_T0, _Sender>>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ __call_result_t<_T1, __call_result_t<_T0, _Sender>>
operator()(_Sender&& __sndr) const&
{
return __t1_(__t0_((_Sender&&)__sndr));
@@ -2913,6 +3481,7 @@
{};
template <sender _Sender, __sender_adaptor_closure_for<_Sender> _Closure>
+STDEXEC_ATTRIBUTE((always_inline)) //
__call_result_t<_Closure, _Sender> operator|(_Sender&& __sndr,
_Closure&& __clsur)
{
@@ -2920,6 +3489,7 @@
}
template <__sender_adaptor_closure _T0, __sender_adaptor_closure _T1>
+STDEXEC_ATTRIBUTE((always_inline)) //
__compose<__decay_t<_T0>, __decay_t<_T1>> operator|(_T0&& __t0, _T1&& __t1)
{
return {{}, (_T0&&)__t0, (_T1&&)__t1};
@@ -2928,18 +3498,19 @@
template <class _Fun, class... _As>
struct __binder_back : sender_adaptor_closure<__binder_back<_Fun, _As...>>
{
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_;
std::tuple<_As...> __as_;
template <sender _Sender>
requires __callable<_Fun, _Sender, _As...>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) //
__call_result_t<_Fun, _Sender, _As...>
operator()(_Sender&& __sndr) && noexcept(
__nothrow_callable<_Fun, _Sender, _As...>)
{
- return std::apply(
- [&__sndr, this](_As&... __as) {
+ return __apply(
+ [&__sndr,
+ this](_As&... __as) -> __call_result_t<_Fun, _Sender, _As...> {
return ((_Fun&&)__fun_)((_Sender&&)__sndr, (_As&&)__as...);
},
__as_);
@@ -2947,13 +3518,14 @@
template <sender _Sender>
requires __callable<const _Fun&, _Sender, const _As&...>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
+ STDEXEC_ATTRIBUTE((host, device))
__call_result_t<const _Fun&, _Sender, const _As&...>
operator()(_Sender&& __sndr) const& //
noexcept(__nothrow_callable<const _Fun&, _Sender, const _As&...>)
{
- return std::apply(
- [&__sndr, this](const _As&... __as) {
+ return __apply(
+ [&__sndr, this](const _As&... __as)
+ -> __call_result_t<const _Fun&, _Sender, const _As&...> {
return __fun_((_Sender&&)__sndr, __as...);
},
__as_);
@@ -2968,9 +3540,8 @@
// A derived-to-base cast that works even when the base is not
// accessible from derived.
template <class _Tp, class _Up>
-STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- __copy_cvref_t<_Up&&, _Tp>
- __c_cast(_Up&& u) noexcept
+STDEXEC_ATTRIBUTE((host, device))
+__copy_cvref_t<_Up&&, _Tp> __c_cast(_Up&& u) noexcept
requires __decays_to<_Tp, _Tp>
{
static_assert(std::is_reference_v<__copy_cvref_t<_Up&&, _Tp>>);
@@ -2984,7 +3555,9 @@
{};
struct __receiver : __nope
-{};
+{
+ using is_receiver = void;
+};
template <same_as<set_error_t> _Tag>
void tag_invoke(_Tag, __receiver, std::exception_ptr) noexcept;
@@ -3006,26 +3579,23 @@
{}
private:
- STDEXEC_NO_UNIQUE_ADDRESS _Base __base_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Base __base_;
protected:
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- _Base&
- base() & noexcept
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) //
+ _Base& base() & noexcept
{
return __base_;
}
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- const _Base&
- base() const& noexcept
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) //
+ const _Base& base() const& noexcept
{
return __base_;
}
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- _Base&&
- base() && noexcept
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) //
+ _Base&& base() && noexcept
{
return (_Base&&)__base_;
}
@@ -3047,8 +3617,8 @@
// but 'int(type::existing_member_function)' is an error (as desired).
#define _DISPATCH_MEMBER(_TAG) \
template <class _Self, class... _Ts> \
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE static auto __call_##_TAG( \
- _Self&& __self, _Ts&&... __ts) noexcept \
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) \
+ static auto __call_##_TAG(_Self&& __self, _Ts&&... __ts) noexcept \
-> decltype(((_Self&&)__self)._TAG((_Ts&&)__ts...)) \
{ \
static_assert(noexcept(((_Self&&)__self)._TAG((_Ts&&)__ts...))); \
@@ -3096,9 +3666,8 @@
using __base_t = __minvoke<__get_base_t, _Dp&&>;
template <class _Dp>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- static __base_t<_Dp>
- __get_base(_Dp&& __self) noexcept
+ STDEXEC_ATTRIBUTE((host, device))
+ static __base_t<_Dp> __get_base(_Dp&& __self) noexcept
{
if constexpr (__has_base)
{
@@ -3111,10 +3680,10 @@
}
template <same_as<set_value_t> _SetValue, class... _As>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- friend auto
- tag_invoke(_SetValue, _Derived&& __self, _As&&... __as) noexcept //
- -> __msecond< //
+ STDEXEC_ATTRIBUTE((host, device, always_inline))
+ friend auto tag_invoke(_SetValue, _Derived&& __self,
+ _As&&... __as) noexcept //
+ -> __msecond< //
__if_c<same_as<set_value_t, _SetValue>>,
decltype(_CALL_MEMBER(set_value, (_Derived&&)__self,
(_As&&)__as...))>
@@ -3128,8 +3697,8 @@
class... _As>
requires _MISSING_MEMBER
(_Dp, set_value) &&
- tag_invocable<_SetValue, __base_t<_Dp>,
- _As...> STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
+ tag_invocable<_SetValue, __base_t<_Dp>, _As...> STDEXEC_ATTRIBUTE(
+ (host, device, always_inline)) //
friend void tag_invoke(_SetValue, _Derived&& __self,
_As&&... __as) noexcept
{
@@ -3137,10 +3706,10 @@
}
template <same_as<set_error_t> _SetError, class _Error>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- friend auto
- tag_invoke(_SetError, _Derived&& __self, _Error&& __err) noexcept //
- -> __msecond< //
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) //
+ friend auto tag_invoke(_SetError, _Derived&& __self,
+ _Error&& __err) noexcept //
+ -> __msecond< //
__if_c<same_as<set_error_t, _SetError>>,
decltype(_CALL_MEMBER(set_error, (_Derived&&)__self,
(_Error&&)__err))>
@@ -3154,8 +3723,8 @@
class _Dp = _Derived>
requires _MISSING_MEMBER
(_Dp, set_error) &&
- tag_invocable<_SetError, __base_t<_Dp>,
- _Error> STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
+ tag_invocable<_SetError, __base_t<_Dp>, _Error> STDEXEC_ATTRIBUTE(
+ (host, device, always_inline)) //
friend void tag_invoke(_SetError, _Derived&& __self,
_Error&& __err) noexcept
{
@@ -3163,10 +3732,9 @@
}
template <same_as<set_stopped_t> _SetStopped, class _Dp = _Derived>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- friend auto
- tag_invoke(_SetStopped, _Derived&& __self) noexcept //
- -> __msecond< //
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) //
+ friend auto tag_invoke(_SetStopped, _Derived&& __self) noexcept //
+ -> __msecond< //
__if_c<same_as<set_stopped_t, _SetStopped>>,
decltype(_CALL_MEMBER(set_stopped, (_Dp&&)__self))>
{
@@ -3178,8 +3746,8 @@
template <same_as<set_stopped_t> _SetStopped, class _Dp = _Derived>
requires _MISSING_MEMBER
(_Dp, set_stopped) &&
- tag_invocable<_SetStopped,
- __base_t<_Dp>> STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
+ tag_invocable<_SetStopped, __base_t<_Dp>> STDEXEC_ATTRIBUTE(
+ (host, device, always_inline)) //
friend void tag_invoke(_SetStopped, _Derived&& __self) noexcept
{
stdexec::set_stopped(__get_base((_Derived&&)__self));
@@ -3187,9 +3755,8 @@
// Pass through the get_env receiver query
template <same_as<get_env_t> _GetEnv, class _Dp = _Derived>
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- friend auto
- tag_invoke(_GetEnv, const _Derived& __self) noexcept
+ STDEXEC_ATTRIBUTE((host, device, always_inline)) //
+ friend auto tag_invoke(_GetEnv, const _Derived& __self) noexcept
-> decltype(_CALL_MEMBER(get_env, (const _Dp&)__self))
{
static_assert(noexcept(_CALL_MEMBER(get_env, __self)));
@@ -3198,7 +3765,7 @@
template <same_as<get_env_t> _GetEnv, class _Dp = _Derived>
requires _MISSING_MEMBER
- (_Dp, get_env) STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
+ (_Dp, get_env) STDEXEC_ATTRIBUTE((host, device, always_inline)) //
friend auto tag_invoke(_GetEnv, const _Derived& __self) noexcept
-> env_of_t<__base_t<const _Dp&>>
{
@@ -3222,7 +3789,7 @@
concept __receiver_of_invoke_result = //
receiver_of<_Receiver, completion_signatures<
__minvoke<__remove<void, __qf<set_value_t>>,
- std::invoke_result_t<_Fun, _As...>>>>;
+ __invoke_result_t<_Fun, _As...>>>>;
template <bool _CanThrow = false, class _Receiver, class _Fun, class... _As>
void __set_value_invoke(_Receiver&& __rcvr, _Fun&& __fun,
@@ -3230,15 +3797,15 @@
{
if constexpr (_CanThrow || __nothrow_invocable<_Fun, _As...>)
{
- if constexpr (same_as<void, std::invoke_result_t<_Fun, _As...>>)
+ if constexpr (same_as<void, __invoke_result_t<_Fun, _As...>>)
{
- std::invoke((_Fun&&)__fun, (_As&&)__as...);
+ __invoke((_Fun&&)__fun, (_As&&)__as...);
set_value((_Receiver&&)__rcvr);
}
else
{
set_value((_Receiver&&)__rcvr,
- std::invoke((_Fun&&)__fun, (_As&&)__as...));
+ __invoke((_Fun&&)__fun, (_As&&)__as...));
}
}
else
@@ -3281,7 +3848,7 @@
};
template <class _Fun, class... _Args>
- requires invocable<_Fun, _Args...>
+ requires __invocable<_Fun, _Args...>
using __non_throwing_ = __mbool<__nothrow_invocable<_Fun, _Args...>>;
template <class _Tag, class _Fun, class _Sender, class _Env, class _Catch>
@@ -3293,10 +3860,40 @@
completion_signatures<>, __with_exception_ptr>;
template <class _Fun, class... _Args>
- requires invocable<_Fun, _Args...>
+ requires __invocable<_Fun, _Args...>
using __set_value_invoke_t = //
completion_signatures<__minvoke<__remove<void, __qf<set_value_t>>,
- std::invoke_result_t<_Fun, _Args...>>>;
+ __invoke_result_t<_Fun, _Args...>>>;
+
+struct __default_get_env_fn
+{
+ // BUGBUG This should hide all but the forwarding queries
+ template <class _Sender>
+ env_of_t<_Sender> operator()(__ignore, __ignore,
+ const _Sender& __child) const noexcept
+ {
+ return get_env(__child);
+ }
+
+ template <class... _Senders>
+ requires(sizeof...(_Senders) != 1)
+ empty_env operator()(__ignore, __ignore, const _Senders&...) const noexcept
+ {
+ return {};
+ }
+};
+
+template <class _Tag>
+struct __with_default_get_env
+{
+ template <sender_expr_for<_Tag> _Sender>
+ static auto get_env(const _Sender& __sndr) noexcept
+ -> __call_result_t<__sexpr_apply_t, const _Sender&,
+ __default_get_env_fn>
+ {
+ return __sexpr_apply(__sndr, __default_get_env_fn());
+ }
+};
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.then]
@@ -3310,7 +3907,7 @@
struct __data
{
_Receiver __rcvr_;
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_;
};
struct __t
@@ -3322,7 +3919,7 @@
// Customize set_value by invoking the invocable and passing the result
// to the downstream receiver
template <__same_as<set_value_t> _Tag, class... _As>
- requires invocable<_Fun, _As...> &&
+ requires __invocable<_Fun, _As...> &&
__receiver_of_invoke_result<_Receiver, _Fun, _As...>
friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept
{
@@ -3346,7 +3943,7 @@
};
};
-template <class _Sender, class _ReceiverId, class _Fun>
+template <class _CvrefSender, class _ReceiverId, class _Fun>
struct __operation
{
using _Receiver = stdexec::__t<_ReceiverId>;
@@ -3357,14 +3954,14 @@
{
using __id = __operation;
typename __receiver_id::__data __data_;
- connect_result_t<_Sender, __receiver_t> __op_;
+ connect_result_t<_CvrefSender, __receiver_t> __op_;
- __t(_Sender&& __sndr, _Receiver __rcvr, _Fun __fun) //
- noexcept(__nothrow_decay_copyable<_Receiver> //
- && __nothrow_decay_copyable<_Fun> //
- && __nothrow_connectable<_Sender, __receiver_t>) :
+ __t(_CvrefSender&& __sndr, _Receiver __rcvr, _Fun __fun) //
+ noexcept(__nothrow_decay_copyable<_Receiver> //
+ && __nothrow_decay_copyable<_Fun> //
+ && __nothrow_connectable<_CvrefSender, __receiver_t>) :
__data_{(_Receiver&&)__rcvr, (_Fun&&)__fun},
- __op_(connect((_Sender&&)__sndr, __receiver_t{&__data_}))
+ __op_(connect((_CvrefSender&&)__sndr, __receiver_t{&__data_}))
{}
friend void tag_invoke(start_t, __t& __self) noexcept
@@ -3378,109 +3975,84 @@
"In stdexec::then(Sender, Function)..."__csz;
using __on_not_callable = __callable_error<__then_context>;
-template <class _SenderId, class _Fun>
-struct __sender
+template <class _Fun, class _CvrefSender, class _Env>
+using __completion_signatures_t = //
+ __try_make_completion_signatures<
+ _CvrefSender, _Env,
+ __with_error_invoke_t<set_value_t, _Fun, _CvrefSender, _Env,
+ __on_not_callable>,
+ __mbind_front<__mtry_catch_q<__set_value_invoke_t, __on_not_callable>,
+ _Fun>>;
+
+template <class _Receiver>
+struct __connect_fn
{
- using _Sender = stdexec::__t<_SenderId>;
- template <class _Receiver>
- using __receiver = stdexec::__t<__receiver<stdexec::__id<_Receiver>, _Fun>>;
- template <class _Self, class _Receiver>
- using __operation =
- stdexec::__t<__operation<__copy_cvref_t<_Self, _Sender>,
- stdexec::__id<_Receiver>, _Fun>>;
+ _Receiver& __rcvr_;
- struct __t
+ template <class _Fun, class _Child>
+ using __operation_t =
+ __t<__operation<_Child, __id<_Receiver>, __decay_t<_Fun>>>;
+
+ template <class _Fun, class _Child>
+ auto operator()(__ignore, _Fun&& __fun, _Child&& __child) const
+ noexcept(__nothrow_constructible_from<__operation_t<_Fun, _Child>,
+ _Child, _Receiver, _Fun>)
+ -> __operation_t<_Fun, _Child>
{
- using __id = __sender;
- using is_sender = void;
- STDEXEC_NO_UNIQUE_ADDRESS _Sender __sndr_;
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
-
- template <class _Self, class _Env>
- using __completion_signatures = //
- __try_make_completion_signatures<
- __copy_cvref_t<_Self, _Sender>, _Env,
- __with_error_invoke_t<set_value_t, _Fun,
- __copy_cvref_t<_Self, _Sender>, _Env,
- __on_not_callable>,
- __mbind_front<
- __mtry_catch_q<__set_value_invoke_t, __on_not_callable>,
- _Fun>>;
-
- template <__decays_to<__t> _Self, receiver _Receiver>
- requires sender_to<__copy_cvref_t<_Self, _Sender>,
- __receiver<_Receiver>>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
- noexcept(__nothrow_constructible_from<
- __operation<_Self, _Receiver>,
- __copy_cvref_t<_Self, _Sender>, _Receiver&&,
- __copy_cvref_t<_Self, _Fun>>)
- -> __operation<_Self, _Receiver>
- {
- return {((_Self&&)__self).__sndr_, (_Receiver&&)__rcvr,
- ((_Self&&)__self).__fun_};
- }
-
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> dependent_completion_signatures<_Env>;
-
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> __completion_signatures<_Self, _Env>
- requires true;
-
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<const _Sender&>
- {
- return get_env(__self.__sndr_);
- }
- };
+ return __operation_t<_Fun, _Child>{(_Child&&)__child,
+ (_Receiver&&)__rcvr_, (_Fun&&)__fun};
+ }
};
-struct then_t
+////////////////////////////////////////////////////////////////////////////////////////////////
+struct then_t : __with_default_get_env<then_t>
{
- template <class _Sender, class _Fun>
- using __sender = __t<__sender<stdexec::__id<__decay_t<_Sender>>, _Fun>>;
-
template <sender _Sender, __movable_value _Fun>
- requires(!__tag_invocable_with_completion_scheduler<then_t, set_value_t,
- _Sender, _Fun>) &&
- (!tag_invocable<then_t, _Sender, _Fun>) &&
- sender<__sender<_Sender, _Fun>>
- __sender<_Sender, _Fun> operator()(_Sender&& __sndr, _Fun __fun) const
+ auto operator()(_Sender&& __sndr, _Fun __fun) const
{
- return __sender<_Sender, _Fun>{(_Sender&&)__sndr, (_Fun&&)__fun};
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<then_t>((_Fun&&)__fun, (_Sender&&)__sndr));
}
- template <sender _Sender, __movable_value _Fun>
- requires __tag_invocable_with_completion_scheduler<then_t, set_value_t,
- _Sender, _Fun>
- sender auto operator()(_Sender&& __sndr, _Fun __fun) const
- noexcept(nothrow_tag_invocable<
- then_t, __completion_scheduler_for<_Sender, set_value_t>,
- _Sender, _Fun>)
- {
- auto __sched = get_completion_scheduler<set_value_t>(get_env(__sndr));
- return tag_invoke(then_t{}, std::move(__sched), (_Sender&&)__sndr,
- (_Fun&&)__fun);
- }
-
- template <sender _Sender, __movable_value _Fun>
- requires(!__tag_invocable_with_completion_scheduler<then_t, set_value_t,
- _Sender, _Fun>) &&
- tag_invocable<then_t, _Sender, _Fun>
- sender auto operator()(_Sender&& __sndr, _Fun __fun) const
- noexcept(nothrow_tag_invocable<then_t, _Sender, _Fun>)
- {
- return tag_invoke(then_t{}, (_Sender&&)__sndr, (_Fun&&)__fun);
- }
-
- template <class _Fun>
+ template <__movable_value _Fun>
+ STDEXEC_ATTRIBUTE((always_inline)) //
__binder_back<then_t, _Fun> operator()(_Fun __fun) const
{
return {{}, {}, {(_Fun&&)__fun}};
}
+
+ using _Sender = __1;
+ using _Fun = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(then_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(_Sender&)),
+ _Sender, _Fun),
+ tag_invoke_t(then_t, _Sender, _Fun)>;
+
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+#endif
+
+ template <sender_expr_for<then_t> _Sender, class _Env>
+ static auto get_completion_signatures(_Sender&&, _Env&&)
+ -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
+ __child_of<_Sender>, _Env>
+ {
+ return {};
+ }
+
+ template <sender_expr_for<then_t> _Sender, receiver _Receiver>
+ static auto connect(_Sender&& __sndr, _Receiver __rcvr) noexcept(
+ __nothrow_callable<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>)
+ -> __call_result_t<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>
+ {
+ return __sexpr_apply((_Sender&&)__sndr,
+ __connect_fn<_Receiver>{__rcvr});
+ }
};
} // namespace __then
@@ -3496,39 +4068,69 @@
{
using _Receiver = stdexec::__t<_ReceiverId>;
+ struct __data
+ {
+ _Receiver __rcvr_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_;
+ };
+
struct __t
{
using is_receiver = void;
using __id = __receiver;
+ __data* __op_;
- _Receiver __rcvr_;
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
-
- template <__one_of<set_value_t, set_stopped_t> _Tag, same_as<__t> _Self,
- class... _Args>
- requires __callable<_Tag, _Receiver, _Args...>
- friend void tag_invoke(_Tag, _Self&& __self, _Args&&... __args) noexcept
- {
- _Tag{}((_Receiver&&)__self.__rcvr_, (_Args&&)__args...);
- }
-
- // Customize set_error by invoking the invocable and passing the result
- // to the base class
- template <same_as<set_error_t> _Tag, __decays_to<__t> _Self,
- class _Error>
- requires invocable<_Fun, _Error> &&
+ // Customize set_value by invoking the invocable and passing the result
+ // to the downstream receiver
+ template <__same_as<set_error_t> _Tag, class _Error>
+ requires __invocable<_Fun, _Error> &&
__receiver_of_invoke_result<_Receiver, _Fun, _Error>
- friend void tag_invoke(_Tag, _Self&& __self, _Error&& __error) noexcept
+ friend void tag_invoke(_Tag, __t&& __self, _Error&& __error) noexcept
{
- stdexec::__set_value_invoke((_Receiver&&)__self.__rcvr_,
- (_Fun&&)__self.__fun_,
+ stdexec::__set_value_invoke((_Receiver&&)__self.__op_->__rcvr_,
+ (_Fun&&)__self.__op_->__fun_,
(_Error&&)__error);
}
- friend env_of_t<_Receiver> tag_invoke(get_env_t,
- const __t& __self) noexcept
+ template <__one_of<set_value_t, set_stopped_t> _Tag, class... _As>
+ requires __callable<_Tag, _Receiver, _As...>
+ friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept
{
- return get_env(__self.__rcvr_);
+ __tag((_Receiver&&)__self.__op_->__rcvr_, (_As&&)__as...);
+ }
+
+ friend auto tag_invoke(get_env_t, const __t& __self) noexcept
+ -> env_of_t<const _Receiver&>
+ {
+ return get_env(__self.__op_->__rcvr_);
+ }
+ };
+};
+
+template <class _CvrefSender, class _ReceiverId, class _Fun>
+struct __operation
+{
+ using _Receiver = stdexec::__t<_ReceiverId>;
+ using __receiver_id = __receiver<_ReceiverId, _Fun>;
+ using __receiver_t = stdexec::__t<__receiver_id>;
+
+ struct __t : __immovable
+ {
+ using __id = __operation;
+ typename __receiver_id::__data __data_;
+ connect_result_t<_CvrefSender, __receiver_t> __op_;
+
+ __t(_CvrefSender&& __sndr, _Receiver __rcvr, _Fun __fun) //
+ noexcept(__nothrow_decay_copyable<_Receiver> //
+ && __nothrow_decay_copyable<_Fun> //
+ && __nothrow_connectable<_CvrefSender, __receiver_t>) :
+ __data_{(_Receiver&&)__rcvr, (_Fun&&)__fun},
+ __op_(connect((_CvrefSender&&)__sndr, __receiver_t{&__data_}))
+ {}
+
+ friend void tag_invoke(start_t, __t& __self) noexcept
+ {
+ start(__self.__op_);
}
};
};
@@ -3537,107 +4139,86 @@
"In stdexec::upon_error(Sender, Function)..."__csz;
using __on_not_callable = __callable_error<__upon_error_context>;
-template <class _SenderId, class _Fun>
-struct __sender
+template <class _Fun, class _CvrefSender, class _Env>
+using __completion_signatures_t = //
+ __try_make_completion_signatures<
+ _CvrefSender, _Env,
+ __with_error_invoke_t<set_error_t, _Fun, _CvrefSender, _Env,
+ __on_not_callable>,
+ __q<__compl_sigs::__default_set_value>,
+ __mbind_front<__mtry_catch_q<__set_value_invoke_t, __on_not_callable>,
+ _Fun>>;
+
+template <class _Receiver>
+struct __connect_fn
{
- using _Sender = stdexec::__t<_SenderId>;
+ _Receiver& __rcvr_;
- template <class _Receiver>
- using __receiver = stdexec::__t<__receiver<__id<_Receiver>, _Fun>>;
+ template <class _Fun, class _Child>
+ using __operation_t =
+ __t<__operation<_Child, __id<_Receiver>, __decay_t<_Fun>>>;
- struct __t
+ template <class _Fun, class _Child>
+ auto operator()(__ignore, _Fun&& __fun, _Child&& __child) const
+ noexcept(__nothrow_constructible_from<__operation_t<_Fun, _Child>,
+ _Child, _Receiver, _Fun>)
+ -> __operation_t<_Fun, _Child>
{
- using __id = __sender;
- using is_sender = void;
-
- STDEXEC_NO_UNIQUE_ADDRESS _Sender __sndr_;
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
-
- template <class _Self, class _Env>
- using __completion_signatures = //
- __try_make_completion_signatures<
- __copy_cvref_t<_Self, _Sender>, _Env,
- __with_error_invoke_t<set_error_t, _Fun,
- __copy_cvref_t<_Self, _Sender>, _Env,
- __on_not_callable>,
- __q<__compl_sigs::__default_set_value>,
- __mbind_front_q<__set_value_invoke_t, _Fun>>;
-
- template <__decays_to<__t> _Self, receiver _Receiver>
- requires sender_to<__copy_cvref_t<_Self, _Sender>,
- __receiver<_Receiver>>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
- noexcept(__nothrow_connectable<__copy_cvref_t<_Self, _Sender>,
- __receiver<_Receiver>>)
- -> connect_result_t<__copy_cvref_t<_Self, _Sender>,
- __receiver<_Receiver>>
- {
- return stdexec::connect(
- ((_Self&&)__self).__sndr_,
- __receiver<_Receiver>{(_Receiver&&)__rcvr,
- ((_Self&&)__self).__fun_});
- }
-
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> dependent_completion_signatures<_Env>;
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> __completion_signatures<_Self, _Env>
- requires true;
-
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<const _Sender&>
- {
- return get_env(__self.__sndr_);
- }
- };
+ return __operation_t<_Fun, _Child>{(_Child&&)__child,
+ (_Receiver&&)__rcvr_, (_Fun&&)__fun};
+ }
};
-struct upon_error_t
+////////////////////////////////////////////////////////////////////////////////////////////////
+struct upon_error_t : __with_default_get_env<upon_error_t>
{
- template <class _Sender, class _Fun>
- using __sender = __t<__sender<stdexec::__id<__decay_t<_Sender>>, _Fun>>;
-
template <sender _Sender, __movable_value _Fun>
- requires __tag_invocable_with_completion_scheduler<
- upon_error_t, set_error_t, _Sender, _Fun>
- sender auto operator()(_Sender&& __sndr, _Fun __fun) const
- noexcept(nothrow_tag_invocable<
- upon_error_t, __completion_scheduler_for<_Sender, set_error_t>,
- _Sender, _Fun>)
+ auto operator()(_Sender&& __sndr, _Fun __fun) const
{
- auto __sched = get_completion_scheduler<set_error_t>(
- get_env(__sndr)); // TODO ADD TEST!
- return tag_invoke(upon_error_t{}, std::move(__sched), (_Sender&&)__sndr,
- (_Fun&&)__fun);
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<upon_error_t>((_Fun&&)__fun, (_Sender&&)__sndr));
}
- template <sender _Sender, __movable_value _Fun>
- requires(!__tag_invocable_with_completion_scheduler<
- upon_error_t, set_error_t, _Sender, _Fun>) &&
- tag_invocable<upon_error_t, _Sender, _Fun>
- sender auto operator()(_Sender&& __sndr, _Fun __fun) const
- noexcept(nothrow_tag_invocable<upon_error_t, _Sender, _Fun>)
- {
- return tag_invoke(upon_error_t{}, (_Sender&&)__sndr, (_Fun&&)__fun);
- }
-
- template <sender _Sender, __movable_value _Fun>
- requires(!__tag_invocable_with_completion_scheduler<
- upon_error_t, set_error_t, _Sender, _Fun>) &&
- (!tag_invocable<upon_error_t, _Sender, _Fun>) &&
- sender<__sender<_Sender, _Fun>>
- __sender<_Sender, _Fun> operator()(_Sender&& __sndr, _Fun __fun) const
- {
- return __sender<_Sender, _Fun>{(_Sender&&)__sndr, (_Fun&&)__fun};
- }
-
- template <class _Fun>
+ template <__movable_value _Fun>
+ STDEXEC_ATTRIBUTE((always_inline)) //
__binder_back<upon_error_t, _Fun> operator()(_Fun __fun) const
{
return {{}, {}, {(_Fun&&)__fun}};
}
+
+ using _Sender = __1;
+ using _Fun = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(upon_error_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(_Sender&)),
+ _Sender, _Fun),
+ tag_invoke_t(upon_error_t, _Sender, _Fun)>;
+
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+#endif
+
+ template <sender_expr_for<upon_error_t> _Sender, class _Env>
+ static auto get_completion_signatures(_Sender&&, _Env&&)
+ -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
+ __child_of<_Sender>, _Env>
+ {
+ return {};
+ }
+
+ template <sender_expr_for<upon_error_t> _Sender, receiver _Receiver>
+ static auto connect(_Sender&& __sndr, _Receiver __rcvr) noexcept(
+ __nothrow_callable<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>)
+ -> __call_result_t<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>
+ {
+ return __sexpr_apply((_Sender&&)__sndr,
+ __connect_fn<_Receiver>{__rcvr});
+ }
};
} // namespace __upon_error
@@ -3653,32 +4234,68 @@
{
using _Receiver = stdexec::__t<_ReceiverId>;
+ struct __data
+ {
+ _Receiver __rcvr_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_;
+ };
+
struct __t
{
using is_receiver = void;
using __id = __receiver;
- _Receiver __rcvr_;
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
+ __data* __op_;
- template <__one_of<set_value_t, set_error_t> _Tag, same_as<__t> _Self,
- class... _Args>
- requires __callable<_Tag, _Receiver, _Args...>
- friend void tag_invoke(_Tag, _Self&& __self, _Args&&... __args) noexcept
- {
- _Tag{}((_Receiver&&)__self.__rcvr_, (_Args&&)__args...);
- }
-
- template <same_as<set_stopped_t> _Tag>
+ // Customize set_value by invoking the invocable and passing the result
+ // to the downstream receiver
+ template <__same_as<set_stopped_t> _Tag>
+ requires __invocable<_Fun> &&
+ __receiver_of_invoke_result<_Receiver, _Fun>
friend void tag_invoke(_Tag, __t&& __self) noexcept
{
- stdexec::__set_value_invoke((_Receiver&&)__self.__rcvr_,
- (_Fun&&)__self.__fun_);
+ stdexec::__set_value_invoke((_Receiver&&)__self.__op_->__rcvr_,
+ (_Fun&&)__self.__op_->__fun_);
}
- friend env_of_t<_Receiver> tag_invoke(get_env_t,
- const __t& __self) noexcept
+ template <__one_of<set_value_t, set_error_t> _Tag, class... _As>
+ requires __callable<_Tag, _Receiver, _As...>
+ friend void tag_invoke(_Tag __tag, __t&& __self, _As&&... __as) noexcept
{
- return get_env(__self.__rcvr_);
+ __tag((_Receiver&&)__self.__op_->__rcvr_, (_As&&)__as...);
+ }
+
+ friend auto tag_invoke(get_env_t, const __t& __self) noexcept
+ -> env_of_t<const _Receiver&>
+ {
+ return get_env(__self.__op_->__rcvr_);
+ }
+ };
+};
+
+template <class _CvrefSender, class _ReceiverId, class _Fun>
+struct __operation
+{
+ using _Receiver = stdexec::__t<_ReceiverId>;
+ using __receiver_id = __receiver<_ReceiverId, _Fun>;
+ using __receiver_t = stdexec::__t<__receiver_id>;
+
+ struct __t : __immovable
+ {
+ using __id = __operation;
+ typename __receiver_id::__data __data_;
+ connect_result_t<_CvrefSender, __receiver_t> __op_;
+
+ __t(_CvrefSender&& __sndr, _Receiver __rcvr, _Fun __fun) //
+ noexcept(__nothrow_decay_copyable<_Receiver> //
+ && __nothrow_decay_copyable<_Fun> //
+ && __nothrow_connectable<_CvrefSender, __receiver_t>) :
+ __data_{(_Receiver&&)__rcvr, (_Fun&&)__fun},
+ __op_(connect((_CvrefSender&&)__sndr, __receiver_t{&__data_}))
+ {}
+
+ friend void tag_invoke(start_t, __t& __self) noexcept
+ {
+ start(__self.__op_);
}
};
};
@@ -3687,109 +4304,88 @@
"In stdexec::upon_stopped(Sender, Function)..."__csz;
using __on_not_callable = __callable_error<__upon_stopped_context>;
-template <class _SenderId, class _Fun>
-struct __sender
+template <class _Fun, class _CvrefSender, class _Env>
+using __completion_signatures_t = //
+ __try_make_completion_signatures<
+ _CvrefSender, _Env,
+ __with_error_invoke_t<set_stopped_t, _Fun, _CvrefSender, _Env,
+ __on_not_callable>,
+ __q<__compl_sigs::__default_set_value>,
+ __q<__compl_sigs::__default_set_error>, __set_value_invoke_t<_Fun>>;
+
+template <class _Receiver>
+struct __connect_fn
{
- using _Sender = stdexec::__t<_SenderId>;
+ _Receiver& __rcvr_;
- template <class _Receiver>
- using __receiver = stdexec::__t<__receiver<__id<_Receiver>, _Fun>>;
+ template <class _Fun, class _Child>
+ using __operation_t =
+ __t<__operation<_Child, __id<_Receiver>, __decay_t<_Fun>>>;
- struct __t
+ template <class _Fun, class _Child>
+ auto operator()(__ignore, _Fun&& __fun, _Child&& __child) const
+ noexcept(__nothrow_constructible_from<__operation_t<_Fun, _Child>,
+ _Child, _Receiver, _Fun>)
+ -> __operation_t<_Fun, _Child>
{
- using __id = __sender;
- using is_sender = void;
-
- STDEXEC_NO_UNIQUE_ADDRESS _Sender __sndr_;
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
-
- template <class _Self, class _Env>
- using __completion_signatures = //
- __try_make_completion_signatures<
- __copy_cvref_t<_Self, _Sender>, _Env,
- __with_error_invoke_t<set_stopped_t, _Fun,
- __copy_cvref_t<_Self, _Sender>, _Env,
- __on_not_callable>,
- __q<__compl_sigs::__default_set_value>,
- __q<__compl_sigs::__default_set_error>,
- __set_value_invoke_t<_Fun>>;
-
- template <__decays_to<__t> _Self, receiver _Receiver>
- requires __receiver_of_invoke_result<_Receiver, _Fun> &&
- sender_to<__copy_cvref_t<_Self, _Sender>,
- __receiver<_Receiver>>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
- noexcept(__nothrow_connectable<_Sender, __receiver<_Receiver>>)
- -> connect_result_t<__copy_cvref_t<_Self, _Sender>,
- __receiver<_Receiver>>
- {
- return stdexec::connect(
- ((_Self&&)__self).__sndr_,
- __receiver<_Receiver>{(_Receiver&&)__rcvr,
- ((_Self&&)__self).__fun_});
- }
-
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> dependent_completion_signatures<_Env>;
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> __completion_signatures<_Self, _Env>
- requires true;
-
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<const _Sender&>
- {
- return get_env(__self.__sndr_);
- }
- };
+ return __operation_t<_Fun, _Child>{(_Child&&)__child,
+ (_Receiver&&)__rcvr_, (_Fun&&)__fun};
+ }
};
-struct upon_stopped_t
+////////////////////////////////////////////////////////////////////////////////////////////////
+struct upon_stopped_t : __with_default_get_env<upon_stopped_t>
{
- template <class _Sender, class _Fun>
- using __sender = __t<__sender<__id<__decay_t<_Sender>>, _Fun>>;
-
template <sender _Sender, __movable_value _Fun>
- requires __tag_invocable_with_completion_scheduler<
- upon_stopped_t, set_stopped_t, _Sender, _Fun> &&
- __callable<_Fun>
- sender auto operator()(_Sender&& __sndr, _Fun __fun) const noexcept(
- nothrow_tag_invocable<
- upon_stopped_t, __completion_scheduler_for<_Sender, set_stopped_t>,
- _Sender, _Fun>)
+ requires __callable<_Fun>
+ auto operator()(_Sender&& __sndr, _Fun __fun) const
{
- auto __sched = get_completion_scheduler<set_stopped_t>(
- get_env(__sndr)); // TODO ADD TEST!
- return tag_invoke(upon_stopped_t{}, std::move(__sched),
- (_Sender&&)__sndr, (_Fun&&)__fun);
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<upon_stopped_t>((_Fun&&)__fun, (_Sender&&)__sndr));
}
- template <sender _Sender, __movable_value _Fun>
- requires(!__tag_invocable_with_completion_scheduler<
- upon_stopped_t, set_stopped_t, _Sender, _Fun>) &&
- tag_invocable<upon_stopped_t, _Sender, _Fun> && __callable<_Fun>
- sender auto operator()(_Sender&& __sndr, _Fun __fun) const
- noexcept(nothrow_tag_invocable<upon_stopped_t, _Sender, _Fun>)
- {
- return tag_invoke(upon_stopped_t{}, (_Sender&&)__sndr, (_Fun&&)__fun);
- }
-
- template <sender _Sender, __movable_value _Fun>
- requires(!__tag_invocable_with_completion_scheduler<
- upon_stopped_t, set_stopped_t, _Sender, _Fun>) &&
- (!tag_invocable<upon_stopped_t, _Sender, _Fun>) &&
- __callable<_Fun> && sender<__sender<_Sender, _Fun>>
- __sender<_Sender, _Fun> operator()(_Sender&& __sndr, _Fun __fun) const
- {
- return __sender<_Sender, _Fun>{(_Sender&&)__sndr, (_Fun&&)__fun};
- }
-
- template <__callable _Fun>
- __binder_back<upon_stopped_t, _Fun> operator()(_Fun __fun) const
+ template <__movable_value _Fun>
+ requires __callable<_Fun>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ __binder_back<upon_stopped_t, _Fun>
+ operator()(_Fun __fun) const
{
return {{}, {}, {(_Fun&&)__fun}};
}
+
+ using _Sender = __1;
+ using _Fun = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(upon_stopped_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(_Sender&)),
+ _Sender, _Fun),
+ tag_invoke_t(upon_stopped_t, _Sender, _Fun)>;
+
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+#endif
+
+ template <sender_expr_for<upon_stopped_t> _Sender, class _Env>
+ static auto get_completion_signatures(_Sender&&, _Env&&)
+ -> __completion_signatures_t<__decay_t<__data_of<_Sender>>,
+ __child_of<_Sender>, _Env>
+ {
+ return {};
+ }
+
+ template <sender_expr_for<upon_stopped_t> _Sender, receiver _Receiver>
+ static auto connect(_Sender&& __sndr, _Receiver __rcvr) noexcept(
+ __nothrow_callable<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>)
+ -> __call_result_t<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>
+ {
+ return __sexpr_apply((_Sender&&)__sndr,
+ __connect_fn<_Receiver>{__rcvr});
+ }
};
} // namespace __upon_stopped
@@ -3804,177 +4400,223 @@
"In stdexec::bulk(Sender, Shape, Function)..."__csz;
using __on_not_callable = __callable_error<__bulk_context>;
-template <class _ReceiverId, integral _Shape, class _Fun>
+template <class _Shape, class _Fun>
+struct __data
+{
+ _Shape __shape_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_;
+ static constexpr auto __mbrs_ =
+ __mliterals<&__data::__shape_, &__data::__fun_>();
+};
+template <class _Shape, class _Fun>
+__data(_Shape, _Fun) -> __data<_Shape, _Fun>;
+
+template <class _ReceiverId, class _Shape, class _Fun>
struct __receiver
{
using _Receiver = stdexec::__t<_ReceiverId>;
- class __t : receiver_adaptor<__t, _Receiver>
+ struct __op_state : __data<_Shape, _Fun>
{
- friend receiver_adaptor<__t, _Receiver>;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_;
+ };
- STDEXEC_NO_UNIQUE_ADDRESS _Shape __shape_;
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __f_;
+ struct __t
+ {
+ using is_receiver = void;
+ using __id = __receiver;
+ __op_state* __op_;
- template <class... _As>
- void set_value(_As&&... __as) && noexcept
+ template <same_as<set_value_t> _Tag, class... _As>
+ requires __callable<_Tag, _Receiver, _As...>
+ friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept
requires __nothrow_callable<_Fun, _Shape, _As&...>
{
- for (_Shape __i{}; __i != __shape_; ++__i)
+ for (_Shape __i{}; __i != __self.__op_->__shape_; ++__i)
{
- __f_(__i, __as...);
+ __self.__op_->__fun_(__i, __as...);
}
- stdexec::set_value(std::move(this->base()), (_As&&)__as...);
+ stdexec::set_value((_Receiver&&)__self.__op_->__rcvr_,
+ (_As&&)__as...);
}
- template <class... _As>
- void set_value(_As&&... __as) && noexcept
+ template <same_as<set_value_t> _Tag, class... _As>
+ requires __callable<_Tag, _Receiver, _As...>
+ friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept
requires __callable<_Fun, _Shape, _As&...>
{
try
{
- for (_Shape __i{}; __i != __shape_; ++__i)
+ for (_Shape __i{}; __i != __self.__op_->__shape_; ++__i)
{
- __f_(__i, __as...);
+ __self.__op_->__fun_(__i, __as...);
}
- stdexec::set_value(std::move(this->base()), (_As&&)__as...);
+ stdexec::set_value((_Receiver&&)__self.__op_->__rcvr_,
+ (_As&&)__as...);
}
catch (...)
{
- stdexec::set_error(std::move(this->base()),
+ stdexec::set_error((_Receiver&&)__self.__op_->__rcvr_,
std::current_exception());
}
}
- public:
- using __id = __receiver;
+ template <same_as<set_error_t> _Tag, class _Error>
+ requires __callable<_Tag, _Receiver, _Error>
+ friend void tag_invoke(_Tag, __t&& __self, _Error&& __error) noexcept
+ {
+ stdexec::set_error((_Receiver&&)__self.__op_->__rcvr_,
+ (_Error&&)__error);
+ }
- explicit __t(_Receiver __rcvr, _Shape __shape, _Fun __fun) :
- receiver_adaptor<__t, _Receiver>((_Receiver&&)__rcvr),
- __shape_(__shape), __f_((_Fun&&)__fun)
+ template <same_as<set_stopped_t> _Tag>
+ requires __callable<_Tag, _Receiver>
+ friend void tag_invoke(_Tag, __t&& __self) noexcept
+ {
+ stdexec::set_stopped((_Receiver&&)__self.__op_->__rcvr_);
+ }
+
+ friend env_of_t<_Receiver> tag_invoke(get_env_t,
+ const __t& __self) noexcept
+ {
+ return stdexec::get_env(__self.__op_->__rcvr_);
+ }
+ };
+};
+
+template <class _CvrefSenderId, class _ReceiverId, class _Shape, class _Fun>
+struct __operation
+{
+ using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
+ using _Receiver = stdexec::__t<_ReceiverId>;
+ using __receiver_id = __receiver<_ReceiverId, _Shape, _Fun>;
+ using __receiver_t = stdexec::__t<__receiver_id>;
+
+ struct __t : __immovable
+ {
+ using __id = __operation;
+ using __data_t = __data<_Shape, _Fun>;
+
+ typename __receiver_id::__op_state __state_;
+ connect_result_t<_CvrefSender, __receiver_t> __op_;
+
+ __t(_CvrefSender&& __sndr, _Receiver __rcvr, __data_t __data) //
+ noexcept(__nothrow_decay_copyable<_Receiver> //
+ && __nothrow_decay_copyable<__data_t> //
+ && __nothrow_connectable<_CvrefSender, __receiver_t>) :
+ __state_{(__data_t&&)__data, (_Receiver&&)__rcvr},
+ __op_(connect((_CvrefSender&&)__sndr, __receiver_t{&__state_}))
{}
+
+ friend void tag_invoke(start_t, __t& __self) noexcept
+ {
+ start(__self.__op_);
+ }
};
};
template <class _Ty>
using __decay_ref = __decay_t<_Ty>&;
-template <class _SenderId, integral _Shape, class _Fun>
-struct __sender
-{
- using _Sender = stdexec::__t<_SenderId>;
-
- template <receiver _Receiver>
- using __receiver =
- stdexec::__t<__receiver<stdexec::__id<_Receiver>, _Shape, _Fun>>;
-
- struct __t
- {
- using __id = __sender;
- using is_sender = void;
-
- STDEXEC_NO_UNIQUE_ADDRESS _Sender __sndr_;
- STDEXEC_NO_UNIQUE_ADDRESS _Shape __shape_;
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
-
- template <class _Sender, class _Env, class _Catch>
- using __with_error_invoke_t = //
- __if<__try_value_types_of_t<
- _Sender, _Env,
- __transform<
- __q<__decay_ref>,
+template <class _CvrefSender, class _Env, class _Shape, class _Fun,
+ class _Catch>
+using __with_error_invoke_t = //
+ __if<__try_value_types_of_t<
+ _CvrefSender, _Env,
+ __transform<__q<__decay_ref>,
__mbind_front<__mtry_catch_q<__non_throwing_, _Catch>,
_Fun, _Shape>>,
- __q<__mand>>,
- completion_signatures<>, __with_exception_ptr>;
+ __q<__mand>>,
+ completion_signatures<>, __with_exception_ptr>;
- template <class _Self, class _Env>
- using __completion_signatures = //
- __try_make_completion_signatures<
- __copy_cvref_t<_Self, _Sender>, _Env,
- __with_error_invoke_t<__copy_cvref_t<_Self, _Sender>, _Env,
- __on_not_callable>>;
+template <class _CvrefSender, class _Env, class _Shape, class _Fun>
+using __completion_signatures = //
+ __try_make_completion_signatures<
+ _CvrefSender, _Env,
+ __with_error_invoke_t<_CvrefSender, _Env, _Shape, _Fun,
+ __on_not_callable>>;
- template <__decays_to<__t> _Self, receiver _Receiver>
- requires sender_to<__copy_cvref_t<_Self, _Sender>,
- __receiver<_Receiver>>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
- noexcept(__nothrow_connectable<__copy_cvref_t<_Self, _Sender>,
- __receiver<_Receiver>>)
- -> connect_result_t<__copy_cvref_t<_Self, _Sender>,
- __receiver<_Receiver>>
- {
- return stdexec::connect(
- ((_Self&&)__self).__sndr_,
- __receiver<_Receiver>{(_Receiver&&)__rcvr, __self.__shape_,
- ((_Self&&)__self).__fun_});
- }
+template <class _Receiver>
+struct __connect_fn
+{
+ _Receiver& __rcvr_;
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> dependent_completion_signatures<_Env>;
+ template <class _Child, class _Data>
+ using __operation_t = //
+ __t<__operation<__cvref_id<_Child>, __id<_Receiver>,
+ decltype(_Data::__shape_), decltype(_Data::__fun_)>>;
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> __completion_signatures<_Self, _Env>
- requires true;
-
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<const _Sender&>
- {
- return get_env(__self.__sndr_);
- }
- };
+ template <class _Data, class _Child>
+ auto operator()(__ignore, _Data __data, _Child&& __child) const
+ noexcept(__nothrow_constructible_from<__operation_t<_Child, _Data>,
+ _Child, _Receiver, _Data>)
+ -> __operation_t<_Child, _Data>
+ {
+ return __operation_t<_Child, _Data>{
+ (_Child&&)__child, (_Receiver&&)__rcvr_, (_Data&&)__data};
+ }
};
-struct bulk_t
+struct bulk_t : __with_default_get_env<bulk_t>
{
- template <sender _Sender, integral _Shape, class _Fun>
- using __sender =
- __t<__sender<stdexec::__id<__decay_t<_Sender>>, _Shape, _Fun>>;
-
template <sender _Sender, integral _Shape, __movable_value _Fun>
- requires __tag_invocable_with_completion_scheduler<
- bulk_t, set_value_t, _Sender, _Shape, _Fun>
- sender auto operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const
- noexcept(nothrow_tag_invocable<
- bulk_t, __completion_scheduler_for<_Sender, set_value_t>,
- _Sender, _Shape, _Fun>)
+ STDEXEC_ATTRIBUTE((host, device))
+ auto operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const
{
- auto __sched = get_completion_scheduler<set_value_t>(get_env(__sndr));
- return tag_invoke(bulk_t{}, std::move(__sched), (_Sender&&)__sndr,
- (_Shape&&)__shape, (_Fun&&)__fun);
- }
-
- template <sender _Sender, integral _Shape, __movable_value _Fun>
- requires(!__tag_invocable_with_completion_scheduler<
- bulk_t, set_value_t, _Sender, _Shape, _Fun>) &&
- tag_invocable<bulk_t, _Sender, _Shape, _Fun>
- sender auto operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const
- noexcept(nothrow_tag_invocable<bulk_t, _Sender, _Shape, _Fun>)
- {
- return tag_invoke(bulk_t{}, (_Sender&&)__sndr, (_Shape&&)__shape,
- (_Fun&&)__fun);
- }
-
- template <sender _Sender, integral _Shape, __movable_value _Fun>
- requires(!__tag_invocable_with_completion_scheduler<
- bulk_t, set_value_t, _Sender, _Shape, _Fun>) &&
- (!tag_invocable<bulk_t, _Sender, _Shape, _Fun>)
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- __sender<_Sender, _Shape, _Fun>
- operator()(_Sender&& __sndr, _Shape __shape, _Fun __fun) const
- {
- return __sender<_Sender, _Shape, _Fun>{(_Sender&&)__sndr, __shape,
- (_Fun&&)__fun};
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<bulk_t>(__data{__shape, (_Fun&&)__fun},
+ (_Sender&&)__sndr));
}
template <integral _Shape, class _Fun>
+ STDEXEC_ATTRIBUTE((always_inline)) //
__binder_back<bulk_t, _Shape, _Fun> operator()(_Shape __shape,
_Fun __fun) const
{
return {{}, {}, {(_Shape&&)__shape, (_Fun&&)__fun}};
}
+
+ // This describes how to use the pieces of a bulk sender to find
+ // legacy customizations of the bulk algorithm.
+ using _Sender = __1;
+ using _Shape = __nth_member<0>(__0);
+ using _Fun = __nth_member<1>(__0);
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(bulk_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(_Sender&)),
+ _Sender, _Shape, _Fun),
+ tag_invoke_t(bulk_t, _Sender, _Shape, _Fun)>;
+
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+#endif
+
+ template <class _Sender>
+ using __fun_t = decltype(__decay_t<__data_of<_Sender>>::__fun_);
+
+ template <class _Sender>
+ using __shape_t = decltype(__decay_t<__data_of<_Sender>>::__shape_);
+
+ template <sender_expr_for<bulk_t> _Sender, class _Env>
+ static auto get_completion_signatures(_Sender&&, _Env&&)
+ -> __completion_signatures<__child_of<_Sender>, _Env,
+ __shape_t<_Sender>, __fun_t<_Sender>>
+ {
+ return {};
+ }
+
+ template <sender_expr_for<bulk_t> _Sender, receiver _Receiver>
+ static auto connect(_Sender&& __sndr, _Receiver __rcvr) noexcept(
+ __nothrow_callable<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>)
+ -> __call_result_t<__sexpr_apply_t, _Sender, __connect_fn<_Receiver>>
+ {
+ return __sexpr_apply((_Sender&&)__sndr,
+ __connect_fn<_Receiver>{__rcvr});
+ }
};
} // namespace __bulk
@@ -4050,7 +4692,7 @@
__notify_fn* __notify_{};
};
-template <class _CvrefSenderId, class _EnvId>
+template <class _CvrefSenderId, class _EnvId = __id<empty_env>>
struct __sh_state
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
@@ -4081,17 +4723,16 @@
using __receiver_ = stdexec::__t<__receiver<_CvrefSenderId, _EnvId>>;
- void* const __token_{(void*)0xDEADBEEF};
in_place_stop_source __stop_source_{};
__variant_t __data_;
std::atomic<void*> __head_{nullptr};
__env_t<_Env> __env_;
connect_result_t<_CvrefSender, __receiver_> __op_state2_;
- explicit __t(_CvrefSender&& __sndr, _Env __env) :
+ explicit __t(_CvrefSender&& __sndr, _Env __env = {}) :
__env_(
- __make_env((_Env&&)__env, __with_(get_stop_token,
- __stop_source_.get_token()))),
+ __make_env((_Env&&)__env, __mkprop(__stop_source_.get_token(),
+ get_stop_token))),
__op_state2_(connect((_CvrefSender&&)__sndr, __receiver_{*this}))
{}
@@ -4114,6 +4755,8 @@
};
template <class _CvrefSenderId, class _EnvId, class _ReceiverId>
+ requires sender_to<__cvref_t<_CvrefSenderId>,
+ __t<__receiver<_CvrefSenderId, _EnvId>>>
struct __operation
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
@@ -4163,7 +4806,7 @@
std::visit(
[&](const auto& __tupl) noexcept -> void {
- std::apply(
+ __apply(
[&](auto __tag, const auto&... __args) noexcept -> void {
__tag((_Receiver&&)__op->__rcvr_, __args...);
},
@@ -4176,7 +4819,6 @@
{
stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>* __shared_state =
__self.__shared_state_.get();
- STDEXEC_ASSERT(__shared_state->__token_ == (void*)0xDEADBEEF);
std::atomic<void*>& __head = __shared_state->__head_;
void* const __completion_state = static_cast<void*>(__shared_state);
void* __old = __head.load(std::memory_order_acquire);
@@ -4219,117 +4861,117 @@
};
};
-template <class _CvrefSenderId, class _EnvId>
-struct __sender
+struct __split_t
{
- using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
- using _Env = stdexec::__t<_EnvId>;
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+#endif
- template <class _Receiver>
- using __operation = stdexec::__t<
- __operation<_CvrefSenderId, _EnvId, stdexec::__id<_Receiver>>>;
+ template <class... _Tys>
+ using __set_value_t =
+ completion_signatures<set_value_t(const __decay_t<_Tys>&...)>;
- struct __t
- {
- using __id = __sender;
- using is_sender = void;
+ template <class _Ty>
+ using __set_error_t =
+ completion_signatures<set_error_t(const __decay_t<_Ty>&)>;
- explicit __t(_CvrefSender&& __sndr, _Env __env) :
- __shared_state_{std::make_shared<__sh_state_>(
- static_cast<_CvrefSender&&>(__sndr), (_Env&&)__env)}
- {}
+ template <class _CvrefSenderId, class _EnvId>
+ using __completions_t = //
+ __try_make_completion_signatures<
+ // NOT TO SPEC:
+ // See
+ // https://github.com/brycelelbach/wg21_p2300_execution/issues/26
+ __cvref_t<_CvrefSenderId>, __env_t<__t<_EnvId>>,
+ completion_signatures<set_error_t(const std::exception_ptr&),
+ set_stopped_t()>, // NOT TO SPEC
+ __q<__set_value_t>, __q<__set_error_t>>;
- private:
- using __sh_state_ = stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>;
-
- template <class... _Tys>
- using __set_value_t =
- completion_signatures<set_value_t(const __decay_t<_Tys>&...)>;
-
- template <class _Ty>
- using __set_error_t =
- completion_signatures<set_error_t(const __decay_t<_Ty>&)>;
-
- template <class _Self>
- using __completions_t = //
- __try_make_completion_signatures<
- // NOT TO SPEC:
- // See
- // https://github.com/brycelelbach/wg21_p2300_execution/issues/26
- _CvrefSender, __env_t<__mfront<_Env, _Self>>,
- completion_signatures<set_error_t(const std::exception_ptr&),
- set_stopped_t()>, // NOT TO SPEC
- __q<__set_value_t>, __q<__set_error_t>>;
-
- std::shared_ptr<__sh_state_> __shared_state_;
-
- template <__decays_to<__t> _Self,
- receiver_of<__completions_t<_Self>> _Receiver>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __recvr) //
- noexcept(std::is_nothrow_move_constructible_v<_Receiver>)
- -> __operation<_Receiver>
- {
- return __operation<_Receiver>{(_Receiver&&)__recvr,
- __self.__shared_state_};
- }
-
- template <__decays_to<__t> _Self, class _OtherEnv>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&,
- _OtherEnv&&) -> __completions_t<_Self>;
+ static inline constexpr auto __connect_fn =
+ []<class _Receiver>(_Receiver& __rcvr) noexcept {
+ return
+ [&]<class _ShState>(auto, std::shared_ptr<_ShState> __sh_state) //
+ noexcept(__nothrow_decay_copyable<_Receiver>)
+ -> __t<__mapply<__mbind_back_q<__operation, __id<_Receiver>>,
+ __id<_ShState>>> {
+ return {(_Receiver&&)__rcvr, std::move(__sh_state)};
+ };
};
-};
-struct split_t;
+ static inline constexpr auto __get_completion_signatures_fn =
+ []<class _ShState>(auto, const std::shared_ptr<_ShState>&) //
+ -> __mapply<__q<__completions_t>, __id<_ShState>> { return {}; };
-// When looking for user-defined customizations of split, these
-// are the signatures to test against, in order:
-using _Sender = __0;
-using _Env = __1;
-using __cust_sigs = //
- __types<tag_invoke_t(split_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(const _Sender&)),
- _Sender),
- tag_invoke_t(split_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(const _Sender&)),
- _Sender, _Env),
- tag_invoke_t(split_t, get_scheduler_t(_Env&), _Sender),
- tag_invoke_t(split_t, get_scheduler_t(_Env&), _Sender, _Env),
- tag_invoke_t(split_t, _Sender),
- tag_invoke_t(split_t, _Sender, _Env)>;
-
-template <class _Sender, class _Env>
-inline constexpr bool __is_split_customized =
- __minvocable<__which<__cust_sigs>, _Sender, _Env>;
-
-template <class _Sender, class _Env>
-using __sender_t =
- __t<__sender<stdexec::__cvref_id<_Sender>, stdexec::__id<__decay_t<_Env>>>>;
-
-template <class _Sender, class _Env>
-using __dispatcher_for =
- __make_dispatcher<__cust_sigs, __mconstructor_for<__sender_t>, _Sender,
- _Env>;
-
-struct split_t
-{
- template <sender _Sender, class _Env = empty_env>
- requires(sender_in<_Sender, _Env> &&
- __decay_copyable<env_of_t<_Sender>>) ||
- __is_split_customized<_Sender, _Env>
- auto operator()(_Sender&& __sndr, _Env&& __env = _Env{}) const noexcept(
- __nothrow_callable<__dispatcher_for<_Sender, _Env>, _Sender, _Env>)
- -> __call_result_t<__dispatcher_for<_Sender, _Env>, _Sender, _Env>
+ template <sender_expr_for<__split_t> _Self, class _Receiver>
+ static auto connect(_Self&& __self, _Receiver __rcvr) noexcept(
+ __nothrow_callable<
+ __sexpr_apply_t, _Self,
+ __call_result_t<__mtypeof<__connect_fn>, _Receiver&>>)
+ -> __call_result_t<__sexpr_apply_t, _Self,
+ __call_result_t<__mtypeof<__connect_fn>, _Receiver&>>
{
- return __dispatcher_for<_Sender, _Env>{}((_Sender&&)__sndr,
- (_Env&&)__env);
+ return __sexpr_apply((_Self&&)__self, __connect_fn(__rcvr));
}
+ template <sender_expr_for<__split_t> _Self, class _OtherEnv>
+ static auto get_completion_signatures(_Self&&, _OtherEnv&&)
+ -> __call_result_t<__sexpr_apply_t, _Self,
+ __mtypeof<__get_completion_signatures_fn>>
+ {
+ return {};
+ }
+};
+
+struct split_t : __split_t
+{
+ template <sender _Sender>
+ requires sender_in<_Sender, empty_env> &&
+ __decay_copyable<env_of_t<_Sender>>
+ auto operator()(_Sender&& __sndr) const
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<split_t>(__(), (_Sender&&)__sndr));
+ }
+
+ template <sender _Sender, class _Env>
+ requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>>
+ auto operator()(_Sender&& __sndr, _Env&& __env) const
+ {
+ auto __domain = __get_late_domain(__sndr, __env);
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<split_t>(__(), (_Sender&&)__sndr),
+ (_Env&&)__env);
+ }
+
+ STDEXEC_ATTRIBUTE((always_inline)) //
__binder_back<split_t> operator()() const
{
return {{}, {}, {}};
}
+
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(split_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(const _Sender&)),
+ _Sender),
+ tag_invoke_t(split_t, _Sender)>;
+
+ template <class _Sender, class... _Env>
+ static auto transform_sender(_Sender&& __sndr, _Env... __env)
+ {
+ return __sexpr_apply(
+ (_Sender&&)__sndr,
+ [&]<class _Child>(__ignore, __ignore, _Child&& __child) {
+ using __sh_state_t =
+ __t<__sh_state<__cvref_id<_Child>, __id<_Env>...>>;
+ auto __sh_state = std::make_shared<__sh_state_t>(
+ (_Child&&)__child, std::move(__env)...);
+ return __make_sexpr<__split_t>(std::move(__sh_state));
+ });
+ }
};
} // namespace __split
@@ -4348,7 +4990,7 @@
template <class _CvrefSenderId, class _EnvId>
struct __sh_state;
-template <class _CvrefSenderId, class _EnvId>
+template <class _CvrefSenderId, class _EnvId = __id<empty_env>>
struct __receiver
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
@@ -4406,7 +5048,7 @@
__notify_fn* __notify_{};
};
-template <class _CvrefSenderId, class _EnvId>
+template <class _CvrefSenderId, class _EnvId = __id<empty_env>>
struct __sh_state
{
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
@@ -4444,10 +5086,10 @@
__env_t<_Env> __env_;
connect_result_t<_CvrefSender, __receiver_t> __op_state2_;
- explicit __t(_CvrefSender&& __sndr, _Env __env) :
+ explicit __t(_CvrefSender&& __sndr, _Env __env = {}) :
__env_(
- __make_env((_Env&&)__env, __with_(get_stop_token,
- __stop_source_.get_token()))),
+ __make_env((_Env&&)__env, __mkprop(__stop_source_.get_token(),
+ get_stop_token))),
__op_state2_(connect((_CvrefSender&&)__sndr, __receiver_t{*this}))
{
start(__op_state2_);
@@ -4533,7 +5175,7 @@
std::visit(
[&](auto& __tupl) noexcept -> void {
- std::apply(
+ __apply(
[&](auto __tag, auto&... __args) noexcept -> void {
__tag((_Receiver&&)__op->__rcvr_, std::move(__args)...);
},
@@ -4587,141 +5229,209 @@
};
};
-template <class _CvrefSenderId, class _EnvId>
-struct __sender
+template <class _ShState>
+struct __data
{
- using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
- using _Env = stdexec::__t<_EnvId>;
+ __data(__intrusive_ptr<_ShState> __sh_state) noexcept :
+ __sh_state_(std::move(__sh_state))
+ {}
- struct __t
+ __data(__data&&) = default;
+ __data& operator=(__data&&) = default;
+
+ ~__data()
{
- using __id = __sender;
- using is_sender = void;
-
- explicit __t(_CvrefSender __sndr, _Env __env) :
- __shared_state_{__make_intrusive<__sh_state_>(
- (_CvrefSender&&)__sndr, (_Env&&)__env)}
- {}
-
- ~__t()
+ if (__sh_state_ != nullptr)
{
- if (nullptr != __shared_state_)
- {
- // We're detaching a potentially running operation. Request
- // cancellation.
- __shared_state_->__detach(); // BUGBUG NOT TO SPEC
- }
+ // detach from the still-running operation.
+ // NOT TO SPEC: This also requests cancellation.
+ __sh_state_->__detach();
}
-
- // Move-only:
- __t(__t&&) = default;
-
- private:
- using __sh_state_ = stdexec::__t<__sh_state<_CvrefSenderId, _EnvId>>;
- template <class _Receiver>
- using __operation = stdexec::__t<
- __operation<_CvrefSenderId, _EnvId, stdexec::__id<_Receiver>>>;
-
- template <class... _Tys>
- using __set_value_t =
- completion_signatures<set_value_t(__decay_t<_Tys>&&...)>;
-
- template <class _Ty>
- using __set_error_t =
- completion_signatures<set_error_t(__decay_t<_Ty>&&)>;
-
- template <class _Self>
- using __completions_t = //
- __try_make_completion_signatures<
- _CvrefSender, __env_t<__mfront<_Env, _Self>>,
- completion_signatures<set_error_t(std::exception_ptr&&),
- set_stopped_t()>, // BUGBUG NOT TO SPEC
- __q<__set_value_t>, __q<__set_error_t>>;
-
- __intrusive_ptr<__sh_state_> __shared_state_;
-
- template <same_as<__t> _Self,
- receiver_of<__completions_t<_Self>> _Receiver>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
- noexcept(std::is_nothrow_move_constructible_v<_Receiver>)
- -> __operation<_Receiver>
- {
- return __operation<_Receiver>{(_Receiver&&)__rcvr,
- std::move(__self).__shared_state_};
- }
-
- template <same_as<__t> _Self, class _OtherEnv>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&,
- _OtherEnv&&) -> __completions_t<_Self>;
- };
-};
-
-struct ensure_started_t;
-
-// When looking for user-defined customizations of ensure_started, these
-// are the signatures to test against, in order:
-using _CvrefSender = __0;
-using _Env = __1;
-using __cust_sigs = //
- __types<tag_invoke_t(ensure_started_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(const _CvrefSender&)),
- _CvrefSender),
- tag_invoke_t(ensure_started_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(const _CvrefSender&)),
- _CvrefSender, _Env),
- tag_invoke_t(ensure_started_t, get_scheduler_t(_Env&),
- _CvrefSender),
- tag_invoke_t(ensure_started_t, get_scheduler_t(_Env&), _CvrefSender,
- _Env),
- tag_invoke_t(ensure_started_t, _CvrefSender),
- tag_invoke_t(ensure_started_t, _CvrefSender, _Env)>;
-
-template <class _CvrefSender, class _Env>
-inline constexpr bool __is_ensure_started_customized =
- __minvocable<__which<__cust_sigs>, _CvrefSender, _Env>;
-
-template <class _Sender, class _Env>
-using __sender_t = __t<__sender<stdexec::__cvref_id<_Sender, _Sender>,
- stdexec::__id<__decay_t<_Env>>>>;
-
-template <class _Sender>
-concept __ensure_started_sender = //
- __is_instance_of<__id<__decay_t<_Sender>>, __sender>;
-
-template <class _Sender>
-using __fallback = __if_c<__ensure_started_sender<_Sender>, __mconst<__first>,
- __mconstructor_for<__sender_t>>;
-
-template <class _Sender, class _Env>
-using __dispatcher_for =
- __make_dispatcher<__cust_sigs, __fallback<_Sender>, _Sender, _Env>;
-
-struct ensure_started_t
-{
- template <sender _Sender, class _Env = empty_env>
- requires(sender_in<_Sender, _Env> &&
- __decay_copyable<env_of_t<_Sender>>) ||
- __is_ensure_started_customized<_Sender, _Env>
- auto operator()(_Sender&& __sndr, _Env&& __env = _Env{}) const noexcept(
- __nothrow_callable<__dispatcher_for<_Sender, _Env>, _Sender, _Env>)
- -> __call_result_t<__dispatcher_for<_Sender, _Env>, _Sender, _Env>
- {
- return __dispatcher_for<_Sender, _Env>{}((_Sender&&)__sndr,
- (_Env&&)__env);
}
+ __intrusive_ptr<_ShState> __sh_state_;
+};
+
+struct __ensure_started_t
+{
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+ friend struct ensure_started_t;
+#endif
+
+ template <class... _Tys>
+ using __set_value_t =
+ completion_signatures<set_value_t(__decay_t<_Tys>&&...)>;
+
+ template <class _Ty>
+ using __set_error_t = completion_signatures<set_error_t(__decay_t<_Ty>&&)>;
+
+ template <class _CvrefSenderId, class _EnvId>
+ using __completions_t = //
+ __try_make_completion_signatures<
+ // NOT TO SPEC:
+ // See
+ // https://github.com/brycelelbach/wg21_p2300_execution/issues/26
+ __cvref_t<_CvrefSenderId>, __env_t<__t<_EnvId>>,
+ completion_signatures<set_error_t(std::exception_ptr&&),
+ set_stopped_t()>, // NOT TO SPEC
+ __q<__set_value_t>, __q<__set_error_t>>;
+
+ static inline constexpr auto __connect_fn =
+ []<class _Receiver>(_Receiver& __rcvr) noexcept {
+ return [&]<class _ShState>(auto, __data<_ShState> __dat) //
+ noexcept(__nothrow_decay_copyable<_Receiver>)
+ -> __t<__mapply<__mbind_back_q<__operation, __id<_Receiver>>,
+ __id<_ShState>>> {
+ return {(_Receiver&&)__rcvr, std::move(__dat.__sh_state_)};
+ };
+ };
+
+ static inline constexpr auto __get_completion_signatures_fn =
+ []<class _ShState>(auto, const __data<_ShState>&) //
+ -> __mapply<__q<__completions_t>, __id<_ShState>> { return {}; };
+
+ template <sender_expr_for<__ensure_started_t> _Self, class _Receiver>
+ static auto connect(_Self&& __self, _Receiver __rcvr) noexcept(
+ __nothrow_callable<
+ __sexpr_apply_t, _Self,
+ __call_result_t<__mtypeof<__connect_fn>, _Receiver&>>)
+ -> __call_result_t<__sexpr_apply_t, _Self,
+ __call_result_t<__mtypeof<__connect_fn>, _Receiver&>>
+ {
+ return __sexpr_apply((_Self&&)__self, __connect_fn(__rcvr));
+ }
+
+ template <sender_expr_for<__ensure_started_t> _Self, class _OtherEnv>
+ static auto get_completion_signatures(_Self&&, _OtherEnv&&)
+ -> __call_result_t<__sexpr_apply_t, _Self,
+ __mtypeof<__get_completion_signatures_fn>>
+ {
+ return {};
+ }
+};
+
+struct ensure_started_t : __with_default_get_env<ensure_started_t>
+{
+ template <sender _Sender>
+ requires sender_in<_Sender, empty_env> &&
+ __decay_copyable<env_of_t<_Sender>>
+ auto operator()(_Sender&& __sndr) const
+ {
+ if constexpr (sender_expr_for<_Sender, __ensure_started_t>)
+ {
+ return (_Sender&&)__sndr;
+ }
+ else
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<ensure_started_t>(__(), (_Sender&&)__sndr));
+ }
+ STDEXEC_UNREACHABLE();
+ }
+
+ template <sender _Sender, class _Env>
+ requires sender_in<_Sender, _Env> && __decay_copyable<env_of_t<_Sender>>
+ auto operator()(_Sender&& __sndr, _Env&& __env) const
+ {
+ if constexpr (sender_expr_for<_Sender, __ensure_started_t>)
+ {
+ return (_Sender&&)__sndr;
+ }
+ else
+ {
+ auto __domain = __get_late_domain(__sndr, __env);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<ensure_started_t>(__(), (_Sender&&)__sndr),
+ (_Env&&)__env);
+ }
+ STDEXEC_UNREACHABLE();
+ }
+
+ STDEXEC_ATTRIBUTE((always_inline)) //
__binder_back<ensure_started_t> operator()() const
{
return {{}, {}, {}};
}
+
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(ensure_started_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(const _Sender&)),
+ _Sender),
+ tag_invoke_t(ensure_started_t, _Sender)>;
+
+ template <class _CvrefSender, class... _Env>
+ using __receiver_t = stdexec::__t<
+ __meval<__receiver, __cvref_id<_CvrefSender>, __id<_Env>...>>;
+
+ template <class _Sender, class... _Env>
+ requires sender_to<__child_of<_Sender>,
+ __receiver_t<__child_of<_Sender>, _Env...>>
+ static auto transform_sender(_Sender&& __sndr, _Env... __env)
+ {
+ return __sexpr_apply(
+ (_Sender&&)__sndr,
+ [&]<class _Child>(__ignore, __ignore, _Child&& __child) {
+ using __sh_state_t =
+ __t<__sh_state<__cvref_id<_Child>, __id<_Env>...>>;
+ auto __sh_state = __make_intrusive<__sh_state_t>(
+ (_Child&&)__child, std::move(__env)...);
+ return __make_sexpr<__ensure_started_t>(
+ __data{std::move(__sh_state)});
+ });
+ }
};
} // namespace __ensure_started
using __ensure_started::ensure_started_t;
inline constexpr ensure_started_t ensure_started{};
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_EDG(not_used_in_partial_spec_arg_list)
+
+/////////////////////////////////////////////////////////////////////////////
+// a receiver adaptor that augments its environment
+namespace __detail
+{
+template <auto _ReceiverPtr, auto... _EnvFns>
+struct __receiver_with;
+
+template <class _Operation, class _Receiver,
+ _Receiver _Operation::*_ReceiverPtr, auto... _EnvFns>
+struct __receiver_with<_ReceiverPtr, _EnvFns...>
+{
+ struct __t : receiver_adaptor<__t>
+ {
+ using __id = __receiver_with;
+ using __env_t =
+ __env::__env_join_t<__result_of<_EnvFns, _Operation*>...,
+ env_of_t<_Receiver>>;
+
+ _Operation* __op_state_;
+
+ _Receiver&& base() && noexcept
+ {
+ return static_cast<_Receiver&&>(__op_state_->*_ReceiverPtr);
+ }
+
+ __env_t get_env() const noexcept
+ {
+ return __join_env(_EnvFns(__op_state_)...,
+ stdexec::get_env(__op_state_->*_ReceiverPtr));
+ }
+ };
+};
+} // namespace __detail
+
+STDEXEC_PRAGMA_POP()
+
//////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.let_value]
// [execution.senders.adaptors.let_error]
@@ -4755,65 +5465,121 @@
template <class _Tp>
using __decay_ref = __decay_t<_Tp>&;
+template <class _ReceiverId, class _SchedulerId>
+struct __operation_base_base_ : __immovable
+{
+ using _Receiver = stdexec::__t<_ReceiverId>;
+ using _Scheduler = stdexec::__t<_SchedulerId>;
+
+ _Receiver __rcvr_; // the input (outer) receiver
+ STDEXEC_ATTRIBUTE((no_unique_address))
+ _Scheduler __sched_; // the input sender's completion scheduler
+};
+
+inline constexpr auto __get_scheduler_prop = [](auto* __op) noexcept {
+ return __mkprop(__op->__sched_, get_scheduler);
+};
+
+inline constexpr auto __get_domain_prop = [](auto*) noexcept {
+ return __mkprop(get_domain);
+};
+
+// A metafunction that computes the result sender type for a given set of
+// argument types
template <class _Fun, class _Set>
-using __result_sender = //
+using __result_sender_t = //
__transform<
__q<__decay_ref>,
__mbind_front<__mtry_catch_q<__call_result_t, __on_not_callable<_Set>>,
_Fun>>;
-template <class _Receiver, class _Fun, class _Set>
-using __op_state_for = __mcompose<__mbind_back_q<connect_result_t, _Receiver>,
- __result_sender<_Fun, _Set>>;
+// FUTURE: when we have a scheduler query for "always completes inline",
+// then we can use that instead of hard-coding `__inln::__scheduler` here.
+template <class _Scheduler>
+concept __unknown_context =
+ __one_of<_Scheduler, __none_such, __inln::__scheduler>;
+
+// The receiver that gets connected to the result sender is the input receiver,
+// possibly augmented with the input sender's completion scheduler (which is
+// where the result sender will be started).
+template <class _Receiver, class _Scheduler>
+using __result_receiver_t = __if_c<
+ __unknown_context<_Scheduler>, _Receiver,
+ __t<__detail::__receiver_with<
+ &__operation_base_base_<__id<_Receiver>, __id<_Scheduler>>::__rcvr_,
+ __get_scheduler_prop, __get_domain_prop>>>;
+
+// If the input sender knows its completion scheduler, make it the current
+// scheduler in the environment seen by the result sender.
+template <class _Env, class _Scheduler>
+using __result_env_t =
+ __if_c<__unknown_context<_Scheduler>, _Env,
+ __env::__env_join_t<__env::__prop<_Scheduler(get_scheduler_t)>,
+ __env::__prop<void(get_domain_t)>, _Env>>;
+
+// A metafunction that computes the type of the resulting operation state for a
+// given set of argument types.
+template <class _Receiver, class _Fun, class _Set, class _Sched>
+using __op_state_for = //
+ __mcompose<__mbind_back_q<connect_result_t,
+ __result_receiver_t<_Receiver, _Sched>>,
+ __result_sender_t<_Fun, _Set>>;
template <class _Set, class _Sig>
struct __tfx_signal_
{
- template <class, class>
+ template <class, class, class>
using __f = completion_signatures<_Sig>;
};
template <class _Set, class... _Args>
struct __tfx_signal_<_Set, _Set(_Args...)>
{
- template <class _Env, class _Fun>
+ template <class _Env, class _Fun, class _Sched>
using __f = //
__try_make_completion_signatures<
- __minvoke<__result_sender<_Fun, _Set>, _Args...>, _Env,
+ __minvoke<__result_sender_t<_Fun, _Set>, _Args...>,
+ __result_env_t<_Env, _Sched>,
// because we don't know if connect-ing the result sender will
// throw:
completion_signatures<set_error_t(std::exception_ptr)>>;
};
-template <class _Env, class _Fun, class _Set, class _Sig>
-using __tfx_signal_t = __minvoke<__tfx_signal_<_Set, _Sig>, _Env, _Fun>;
+// `_Sched` is the input sender's completion scheduler, or __none_such if it
+// doesn't have one.
+template <class _Env, class _Fun, class _Set, class _Sched, class _Sig>
+using __tfx_signal_t = __minvoke<__tfx_signal_<_Set, _Sig>, _Env, _Fun, _Sched>;
-template <class _ReceiverId, class _Fun, class _Set, class... _Tuples>
+template <class _ReceiverId, class _Fun, class _Set, class _SchedId,
+ class... _Tuples>
struct __operation_base_
{
using _Receiver = stdexec::__t<_ReceiverId>;
+ using _Sched = stdexec::__t<_SchedId>;
- struct __t : __immovable
+ struct __t : __operation_base_base_<_ReceiverId, _SchedId>
{
using __id = __operation_base_;
using __results_variant_t = std::variant<std::monostate, _Tuples...>;
using __op_state_variant_t = //
__minvoke<
- __transform<__uncurry<__op_state_for<_Receiver, _Fun, _Set>>,
- __nullable_variant_t>,
+ __transform<
+ __uncurry<__op_state_for<_Receiver, _Fun, _Set, _Sched>>,
+ __nullable_variant_t>,
_Tuples...>;
- _Receiver __rcvr_;
_Fun __fun_;
__results_variant_t __args_;
__op_state_variant_t __op_state3_;
};
};
-template <class _ReceiverId, class _Fun, class _Set, class... _Tuples>
+template <class _ReceiverId, class _Fun, class _Set, class _SchedId,
+ class... _Tuples>
struct __receiver_
{
using _Receiver = stdexec::__t<_ReceiverId>;
+ using _Sched = stdexec::__t<_SchedId>;
using _Env = env_of_t<_Receiver>;
struct __t
@@ -4821,28 +5587,41 @@
using is_receiver = void;
using __id = __receiver_;
+ decltype(auto) __get_result_receiver() noexcept
+ {
+ if constexpr (same_as<__result_receiver_t<_Receiver, _Sched>,
+ _Receiver>)
+ {
+ return static_cast<_Receiver&&>(__op_state_->__rcvr_);
+ }
+ else
+ {
+ return __result_receiver_t<_Receiver, _Sched>{{}, __op_state_};
+ }
+ }
+
template <__one_of<_Set> _Tag, class... _As>
requires(1 == __v<__minvoke<__mcount<__decayed_tuple<_As...>>,
_Tuples...>>) &&
- __minvocable<__result_sender<_Fun, _Set>, _As...> &&
- sender_to<__minvoke<__result_sender<_Fun, _Set>, _As...>,
- _Receiver>
+ __minvocable<__result_sender_t<_Fun, _Set>, _As...> &&
+ sender_to<__minvoke<__result_sender_t<_Fun, _Set>, _As...>,
+ __result_receiver_t<_Receiver, _Sched>>
friend void tag_invoke(_Tag, __t&& __self, _As&&... __as) noexcept
{
try
{
using __tuple_t = __decayed_tuple<_As...>;
using __op_state_t =
- __minvoke<__op_state_for<_Receiver, _Fun, _Set>, _As...>;
+ __minvoke<__op_state_for<_Receiver, _Fun, _Set, _Sched>,
+ _As...>;
auto& __args =
__self.__op_state_->__args_.template emplace<__tuple_t>(
(_As&&)__as...);
auto& __op = __self.__op_state_->__op_state3_
.template emplace<__op_state_t>(__conv{[&] {
return connect(
- std::apply(std::move(__self.__op_state_->__fun_),
- __args),
- std::move(__self.__op_state_->__rcvr_));
+ __apply(std::move(__self.__op_state_->__fun_), __args),
+ __self.__get_result_receiver());
}});
start(__op);
}
@@ -4868,17 +5647,23 @@
}
using __operation_base_t = stdexec::__t<
- __operation_base_<_ReceiverId, _Fun, _Set, _Tuples...>>;
+ __operation_base_<_ReceiverId, _Fun, _Set, _SchedId, _Tuples...>>;
__operation_base_t* __op_state_;
};
};
+template <class _Sender, class _Set>
+using __completion_sched = __query_result_or_t<get_completion_scheduler_t<_Set>,
+ env_of_t<_Sender>, __none_such>;
+
template <class _CvrefSenderId, class _ReceiverId, class _Fun, class _Set>
using __receiver = //
stdexec::__t<__gather_completions_for<
_Set, __cvref_t<_CvrefSenderId>, env_of_t<__t<_ReceiverId>>,
__q<__decayed_tuple>,
- __munique<__mbind_front_q<__receiver_, _ReceiverId, _Fun, _Set>>>>;
+ __munique<__mbind_front_q<
+ __receiver_, _ReceiverId, _Fun, _Set,
+ __id<__completion_sched<__cvref_t<_CvrefSenderId>, _Set>>>>>>;
template <class _CvrefSenderId, class _ReceiverId, class _Fun, class _Set>
using __operation_base = typename __receiver<_CvrefSenderId, _ReceiverId, _Fun,
@@ -4904,7 +5689,13 @@
template <class _Receiver2>
__t(_Sender&& __sndr, _Receiver2&& __rcvr, _Fun __fun) :
- __op_base_t{{}, (_Receiver2&&)__rcvr, (_Fun&&)__fun},
+ __op_base_t{{{},
+ (_Receiver2&&)__rcvr,
+ query_or(get_completion_scheduler<_Set>,
+ get_env(__sndr), __none_such())},
+ (_Fun&&)__fun,
+ {},
+ {}},
__op_state2_(connect((_Sender&&)__sndr, __receiver_t{this}))
{}
@@ -4912,109 +5703,167 @@
};
};
-template <class _SenderId, class _Fun, class _SetId>
+template <class _Sender, class _Fun, class _SetId>
+struct __sender_base
+{
+ using _Set = stdexec::__t<_SetId>;
+ using is_sender = void;
+
+ template <class _Self, class _Receiver>
+ using __operation_t = //
+ stdexec::__t<__operation<stdexec::__cvref_id<_Self, _Sender>,
+ stdexec::__id<_Receiver>, _Fun, _Set>>;
+ template <class _Self, class _Receiver>
+ using __receiver_t = __receiver<stdexec::__cvref_id<_Self, _Sender>,
+ stdexec::__id<_Receiver>, _Fun, _Set>;
+
+ template <class _CvrefSender>
+ using __completion_sched =
+ __query_result_or_t<get_completion_scheduler_t<_Set>,
+ env_of_t<_CvrefSender>, __none_such>;
+
+ template <class _CvrefSender, class _Env>
+ using __completions = //
+ __mapply<__transform<__mbind_front_q<__tfx_signal_t, _Env, _Fun, _Set,
+ __completion_sched<_Sender>>,
+ __q<__concat_completion_signatures_t>>,
+ __completion_signatures_of_t<_Sender, _Env>>;
+
+ template <__decays_to_derived_from<__sender_base> _Self, receiver _Receiver>
+ requires sender_to<__copy_cvref_t<_Self, _Sender>,
+ __receiver_t<_Self, _Receiver>>
+ friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr)
+ -> __operation_t<_Self, _Receiver>
+ {
+ return __operation_t<_Self, _Receiver>{((_Self&&)__self).__sndr_,
+ (_Receiver&&)__rcvr,
+ ((_Self&&)__self).__fun_};
+ }
+
+ template <__decays_to_derived_from<__sender_base> _Self, class _Env>
+ friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
+ -> __completions<__copy_cvref_t<_Self, _Sender>, _Env>
+ {
+ return {};
+ }
+
+ // BUGBUG better would be to port the `let_[value|error|stopped]` algorithms
+ // to __sexpr
+ template <class _Self, class _ApplyFun>
+ static auto apply(_Self&& __self, _ApplyFun __fun) -> __call_result_t<
+ _ApplyFun,
+ _SetId, // Actually one of let_value_t, let_error_t, let_stopped_t
+ __copy_cvref_t<_Self, _Fun>, __copy_cvref_t<_Self, _Sender>>
+ {
+ return ((_ApplyFun&&)__fun)(_SetId(), ((_Self&&)__self).__fun_,
+ ((_Self&&)__self).__sndr_);
+ }
+
+ _Sender __sndr_;
+ _Fun __fun_;
+};
+
+template <class _SenderId, class _Fun, class _SetId,
+ class _Domain = dependent_domain>
struct __sender
{
- using _Sender = stdexec::__t<_SenderId>;
- using _Set = stdexec::__t<_SetId>;
-
- struct __t
+ struct __t : __sender_base<stdexec::__t<_SenderId>, _Fun, _SetId>
{
using __id = __sender;
- using is_sender = void;
-
- template <class _Self, class _Receiver>
- using __operation_t = //
- stdexec::__t<__operation<stdexec::__cvref_id<_Self, _Sender>,
- stdexec::__id<_Receiver>, _Fun, _Set>>;
- template <class _Self, class _Receiver>
- using __receiver_t = __receiver<stdexec::__cvref_id<_Self, _Sender>,
- stdexec::__id<_Receiver>, _Fun, _Set>;
-
- template <class _Sender, class _Env>
- using __completions = //
- __mapply<
- __transform<__mbind_front_q<__tfx_signal_t, _Env, _Fun, _Set>,
- __q<__concat_completion_signatures_t>>,
- __completion_signatures_of_t<_Sender, _Env>>;
-
- template <__decays_to<__t> _Self, receiver _Receiver>
- requires sender_to<__copy_cvref_t<_Self, _Sender>,
- __receiver_t<_Self, _Receiver>>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr)
- -> __operation_t<_Self, _Receiver>
- {
- return __operation_t<_Self, _Receiver>{((_Self&&)__self).__sndr_,
- (_Receiver&&)__rcvr,
- ((_Self&&)__self).__fun_};
- }
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<const _Sender&>
- {
- return get_env(__self.__sndr_);
+ /*-> env_of_t<const _Sender&>*/ {
+ return __join_env(__mkprop(_Domain(), get_domain),
+ get_env(__self.__sndr_));
}
-
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> dependent_completion_signatures<_Env>;
-
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> __completions<__copy_cvref_t<_Self, _Sender>, _Env>
- requires true;
-
- _Sender __sndr_;
- _Fun __fun_;
};
};
template <class _LetTag, class _SetTag>
-struct __let_xxx_t
+struct __let_xxx_t : __with_default_get_env<_LetTag>
{
+ using _Sender = __1;
+ using _Function = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(_LetTag,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(const _Sender&)),
+ _Sender, _Function),
+ tag_invoke_t(_LetTag, _Sender, _Function)>;
+
using __t = _SetTag;
- template <class _Sender, class _Fun>
- using __sender = stdexec::__t<
- __let::__sender<stdexec::__id<__decay_t<_Sender>>, _Fun, _LetTag>>;
+ template <class _Sender, class _Fun, class _Domain = dependent_domain>
+ using __sender =
+ stdexec::__t<__let::__sender<stdexec::__id<__decay_t<_Sender>>, _Fun,
+ _LetTag, _Domain>>;
template <sender _Sender, __movable_value _Fun>
- requires __tag_invocable_with_completion_scheduler<_LetTag, set_value_t,
- _Sender, _Fun>
- sender auto operator()(_Sender&& __sndr, _Fun __fun) const
- noexcept(nothrow_tag_invocable<
- _LetTag, __completion_scheduler_for<_Sender, set_value_t>,
- _Sender, _Fun>)
+ auto operator()(_Sender&& __sndr, _Fun __fun) const
{
- auto __sched = get_completion_scheduler<set_value_t>(get_env(__sndr));
- return tag_invoke(_LetTag{}, std::move(__sched), (_Sender&&)__sndr,
- (_Fun&&)__fun);
- }
-
- template <sender _Sender, __movable_value _Fun>
- requires(!__tag_invocable_with_completion_scheduler<
- _LetTag, set_value_t, _Sender, _Fun>) &&
- tag_invocable<_LetTag, _Sender, _Fun>
- sender auto operator()(_Sender&& __sndr, _Fun __fun) const
- noexcept(nothrow_tag_invocable<_LetTag, _Sender, _Fun>)
- {
- return tag_invoke(_LetTag{}, (_Sender&&)__sndr, (_Fun&&)__fun);
- }
-
- template <sender _Sender, __movable_value _Fun>
- requires(!__tag_invocable_with_completion_scheduler<
- _LetTag, set_value_t, _Sender, _Fun>) &&
- (!tag_invocable<_LetTag, _Sender, _Fun>) &&
- sender<__sender<_Sender, _Fun>>
- __sender<_Sender, _Fun> operator()(_Sender&& __sndr, _Fun __fun) const
- {
- return __sender<_Sender, _Fun>{(_Sender&&)__sndr, (_Fun&&)__fun};
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __sender<_Sender, _Fun>{{(_Sender&&)__sndr, (_Fun&&)__fun}});
}
template <class _Fun>
+ STDEXEC_ATTRIBUTE((always_inline)) //
__binder_back<_LetTag, _Fun> operator()(_Fun __fun) const
{
return {{}, {}, {(_Fun&&)__fun}};
}
+
+ // Compute all the domains of all the result senders and make sure they're
+ // all the same
+ template <class _Child, class _Fun, class _Env>
+ using __result_domain_t = __gather_completions_for<
+ _SetTag, _Child, _Env,
+ __mtry_catch<__mbind_front_q<__call_result_t, _Fun>,
+ __on_not_callable<_SetTag>>,
+ __q<__domain::__common_domain_t>>;
+
+ static auto get_env(__ignore) noexcept
+ {
+ return __mkprop(dependent_domain(), get_domain);
+ }
+
+ template <sender_expr_for<_LetTag> _Sender, class _Env>
+ static decltype(auto) transform_env(_Sender&& __sndr, const _Env& __env)
+ {
+ return __sexpr_apply(
+ (_Sender&&)__sndr,
+ [&]<class _Fun, sender_in<_Env> _Child>(
+ __ignore, _Fun&&, _Child&& __child) -> decltype(auto) {
+ using _Scheduler = __completion_sched<_Child, _SetTag>;
+ if constexpr (__unknown_context<_Scheduler>)
+ {
+ return (__env);
+ }
+ else
+ {
+ return __join_env(__mkprop(get_completion_scheduler<_SetTag>(
+ stdexec::get_env(__child)),
+ get_scheduler),
+ __mkprop(get_domain), __env);
+ }
+ STDEXEC_UNREACHABLE();
+ });
+ }
+
+ template <sender_expr_for<_LetTag> _Sender, class _Env>
+ requires same_as<__early_domain_of_t<_Sender>, dependent_domain>
+ static decltype(auto) transform_sender(_Sender&& __sndr, const _Env& __env)
+ {
+ return __sexpr_apply((_Sender&&)__sndr,
+ [&]<class _Fun, sender_in<_Env> _Child>(
+ __ignore, _Fun&& __fun, _Child&& __child) {
+ // TODO: propagate errors here
+ using _Domain = __result_domain_t<_Child, _Fun, _Env>;
+ static_assert(__none_of<_Domain, __none_such, dependent_domain>);
+ return __sender<_Child, _Fun, _Domain>{
+ {(_Child&&)__child, (_Fun&&)__fun}};
+ });
+ }
};
struct let_value_t : __let::__let_xxx_t<let_value_t, set_value_t>
@@ -5034,6 +5883,14 @@
using __let::let_stopped_t;
inline constexpr let_stopped_t let_stopped{};
+// BUGBUG this will also be unnecessary when `on` returns a __sexpr
+namespace __detail
+{
+template <class _SenderId, class _Fun, class _SetId>
+extern __mconst<__let::__sender<__name_of<__t<_SenderId>>, _Fun, _SetId>>
+ __name_of_v<__let::__sender<_SenderId, _Fun, _SetId>>;
+}
+
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.stopped_as_optional]
// [execution.senders.adaptors.stopped_as_error]
@@ -5048,46 +5905,51 @@
using _Sender = stdexec::__t<_CvrefSenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
- struct __t : receiver_adaptor<__t>
+ struct __t
{
+ using is_receiver = void;
using __id = __receiver;
- _Receiver&& base() && noexcept
- {
- return (_Receiver&&)__op_->__rcvr_;
- }
-
- const _Receiver& base() const& noexcept
- {
- return __op_->__rcvr_;
- }
-
- template <class _Ty>
- void set_value(_Ty&& __a) && noexcept
+ template <same_as<set_value_t> _Tag, class _Ty>
+ friend void tag_invoke(_Tag, __t&& __self, _Ty&& __a) noexcept
{
try
{
using _Value = __decay_t<
__single_sender_value_t<_Sender, env_of_t<_Receiver>>>;
static_assert(constructible_from<_Value, _Ty>);
- stdexec::set_value(((__t&&)*this).base(),
+ stdexec::set_value((_Receiver&&)__self.__op_->__rcvr_,
std::optional<_Value>{(_Ty&&)__a});
}
catch (...)
{
- stdexec::set_error(((__t&&)*this).base(),
+ stdexec::set_error((_Receiver&&)__self.__op_->__rcvr_,
std::current_exception());
}
}
- void set_stopped() && noexcept
+ template <same_as<set_error_t> _Tag, class _Error>
+ friend void tag_invoke(_Tag, __t&& __self, _Error&& __error) noexcept
+ {
+ stdexec::set_error((_Receiver&&)__self.__op_->__rcvr_,
+ (_Error&&)__error);
+ }
+
+ template <same_as<set_stopped_t> _Tag>
+ friend void tag_invoke(_Tag, __t&& __self) noexcept
{
using _Value = __decay_t<
__single_sender_value_t<_Sender, env_of_t<_Receiver>>>;
- stdexec::set_value(((__t&&)*this).base(),
+ stdexec::set_value((_Receiver&&)__self.__op_->__rcvr_,
std::optional<_Value>{std::nullopt});
}
+ template <same_as<get_env_t> _Tag>
+ friend env_of_t<_Receiver> tag_invoke(_Tag, const __t& __self) noexcept
+ {
+ return stdexec::get_env(__self.__op_->__rcvr_);
+ }
+
stdexec::__t<__operation<_CvrefSenderId, _ReceiverId>>* __op_;
};
};
@@ -5105,7 +5967,7 @@
__t(_Sender&& __sndr, _Receiver&& __rcvr) :
__rcvr_((_Receiver&&)__rcvr),
- __op_state_(connect((_Sender&&)__sndr, __receiver_t{{}, this}))
+ __op_state_(connect((_Sender&&)__sndr, __receiver_t{this}))
{}
STDEXEC_IMMOVABLE(__t);
@@ -5120,74 +5982,77 @@
};
};
-template <class _SenderId>
-struct __sender
-{
- using _Sender = stdexec::__t<_SenderId>;
-
- struct __t
- {
- using __id = __sender;
- using is_sender = void;
-
- template <class _Self, class _Receiver>
- using __operation_t =
- stdexec::__t<__operation<stdexec::__cvref_id<_Self, _Sender>,
- stdexec::__id<_Receiver>>>;
- template <class _Self, class _Receiver>
- using __receiver_t =
- stdexec::__t<__receiver<stdexec::__cvref_id<_Self, _Sender>,
- stdexec::__id<_Receiver>>>;
-
- template <__decays_to<__t> _Self, receiver _Receiver>
- requires __single_typed_sender<__copy_cvref_t<_Self, _Sender>,
- env_of_t<_Receiver>> &&
- sender_to<__copy_cvref_t<_Self, _Sender>,
- __receiver_t<_Self, _Receiver>>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr)
- -> __operation_t<_Self, _Receiver>
- {
- return {((_Self&&)__self).__sndr_, (_Receiver&&)__rcvr};
- }
-
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<const _Sender&>
- {
- return get_env(__self.__sndr_);
- }
-
- template <class... _Tys>
- requires(sizeof...(_Tys) == 1)
- using __set_value_t = completion_signatures<set_value_t(
- std::optional<__decay_t<_Tys>>...)>;
-
- template <class _Ty>
- using __set_error_t = completion_signatures<set_error_t(_Ty)>;
-
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> make_completion_signatures<
- __copy_cvref_t<_Self, _Sender>, _Env,
- completion_signatures<set_error_t(std::exception_ptr)>,
- __set_value_t, __set_error_t, completion_signatures<>>;
-
- _Sender __sndr_;
- };
-};
-
-struct stopped_as_optional_t
+struct stopped_as_optional_t : __with_default_get_env<stopped_as_optional_t>
{
template <sender _Sender>
auto operator()(_Sender&& __sndr) const
- -> __t<__sender<stdexec::__id<__decay_t<_Sender>>>>
{
- return {(_Sender&&)__sndr};
+ return __make_sexpr<stopped_as_optional_t>(__(), (_Sender&&)__sndr);
}
+ STDEXEC_ATTRIBUTE((always_inline)) //
__binder_back<stopped_as_optional_t> operator()() const noexcept
{
return {};
}
+
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+#endif
+
+ template <class _CvrefSender, class _Receiver>
+ using __operation_t =
+ stdexec::__t<__operation<stdexec::__cvref_id<_CvrefSender>,
+ stdexec::__id<_Receiver>>>;
+ template <class _CvrefSender, class _Receiver>
+ using __receiver_t =
+ stdexec::__t<__receiver<stdexec::__cvref_id<_CvrefSender>,
+ stdexec::__id<_Receiver>>>;
+
+ template <class _Receiver>
+ struct __connect_fn
+ {
+ _Receiver __rcvr_;
+
+ template <class _Child>
+ __operation_t<_Child, _Receiver> operator()(stopped_as_optional_t, __,
+ _Child&& __child)
+ {
+ return {(_Child&&)__child, std::move(__rcvr_)};
+ }
+ };
+
+ template <sender_expr_for<stopped_as_optional_t> _Self, receiver _Receiver>
+ requires __single_typed_sender<__child_of<_Self>,
+ env_of_t<_Receiver>> &&
+ sender_to<__child_of<_Self>,
+ __receiver_t<__child_of<_Self>, _Receiver>>
+ static __operation_t<__child_of<_Self>, _Receiver> connect(_Self&& __self,
+ _Receiver __rcvr)
+ {
+ return __sexpr_apply((_Self&&)__self,
+ __connect_fn<_Receiver>{(_Receiver&&)__rcvr});
+ }
+
+ template <class... _Tys>
+ requires(sizeof...(_Tys) == 1)
+ using __set_value_t =
+ completion_signatures<set_value_t(std::optional<__decay_t<_Tys>>...)>;
+
+ template <class _Ty>
+ using __set_error_t = completion_signatures<set_error_t(_Ty)>;
+
+ template <sender_expr_for<stopped_as_optional_t> _Self, class _Env>
+ static auto get_completion_signatures(_Self&&, _Env&&)
+ -> make_completion_signatures<
+ __child_of<_Self>, _Env,
+ completion_signatures<set_error_t(std::exception_ptr)>,
+ __set_value_t, __set_error_t, completion_signatures<>>
+ {
+ return {};
+ }
};
struct stopped_as_error_t
@@ -5204,6 +6069,7 @@
}
template <__movable_value _Error>
+ STDEXEC_ATTRIBUTE((always_inline)) //
auto operator()(_Error __err) const
-> __binder_back<stopped_as_error_t, _Error>
{
@@ -5249,7 +6115,7 @@
using __id = __operation;
run_loop* __loop_;
- STDEXEC_NO_UNIQUE_ADDRESS _Receiver __rcvr_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_;
static void __execute_impl(__task* __p) noexcept
{
@@ -5497,8 +6363,8 @@
__mbind_front_q<__bind_completions_t, _Sender, _Env>>,
set_value_t, set_error_t, set_stopped_t>>;
-template <class _SchedulerId, class _CvrefSenderId, class _ReceiverId>
-struct __operation1;
+template <class _SchedulerId, class _VariantId, class _ReceiverId>
+struct __operation1_base;
// This receiver is to be completed on the execution context
// associated with the scheduler. When the source sender
@@ -5507,7 +6373,7 @@
// read the completion out of the operation state and forward it
// to the output receiver after transitioning to the scheduler's
// context.
-template <class _SchedulerId, class _CvrefSenderId, class _ReceiverId>
+template <class _SchedulerId, class _VariantId, class _ReceiverId>
struct __receiver2
{
using _Receiver = stdexec::__t<_ReceiverId>;
@@ -5516,8 +6382,7 @@
{
using is_receiver = void;
using __id = __receiver2;
- stdexec::__t<__operation1<_SchedulerId, _CvrefSenderId, _ReceiverId>>*
- __op_state_;
+ __operation1_base<_SchedulerId, _VariantId, _ReceiverId>* __op_state_;
// If the work is successfully scheduled on the new execution
// context and is ready to run, forward the completion signal in
@@ -5550,19 +6415,19 @@
// context of the scheduler. That second receiver will read the
// completion information out of the operation state and propagate
// it to the output receiver from within the desired context.
-template <class _SchedulerId, class _CvrefSenderId, class _ReceiverId>
+template <class _SchedulerId, class _VariantId, class _ReceiverId>
struct __receiver1
{
using _Scheduler = stdexec::__t<_SchedulerId>;
using _Receiver = stdexec::__t<_ReceiverId>;
using __receiver2_t =
- stdexec::__t<__receiver2<_SchedulerId, _CvrefSenderId, _ReceiverId>>;
+ stdexec::__t<__receiver2<_SchedulerId, _VariantId, _ReceiverId>>;
struct __t
{
+ using __id = __receiver1;
using is_receiver = void;
- stdexec::__t<__operation1<_SchedulerId, _CvrefSenderId, _ReceiverId>>*
- __op_state_;
+ __operation1_base<_SchedulerId, _VariantId, _ReceiverId>* __op_state_;
template <class... _Args>
static constexpr bool __nothrow_complete_ =
@@ -5589,8 +6454,8 @@
_Args&&... __args) noexcept
{
__try_call((_Receiver&&)__self.__op_state_->__rcvr_,
- __fun_c<__complete_<_Tag, _Args...>>, (_Tag&&)__tag,
- (__t&&)__self, (_Args&&)__args...);
+ __function_constant_v<__complete_<_Tag, _Args...>>,
+ (_Tag&&)__tag, (__t&&)__self, (_Args&&)__args...);
}
friend auto tag_invoke(get_env_t, const __t& __self) noexcept
@@ -5601,63 +6466,74 @@
};
};
+template <class _SchedulerId, class _VariantId, class _ReceiverId>
+struct __operation1_base : __immovable
+{
+ using _Scheduler = stdexec::__t<_SchedulerId>;
+ using _Receiver = stdexec::__t<_ReceiverId>;
+ using _Variant = stdexec::__t<_VariantId>;
+ using __receiver2_t =
+ stdexec::__t<__receiver2<_SchedulerId, _VariantId, _ReceiverId>>;
+
+ _Scheduler __sched_;
+ _Receiver __rcvr_;
+ _Variant __data_;
+ connect_result_t<schedule_result_t<_Scheduler>, __receiver2_t> __state2_;
+
+ __operation1_base(_Scheduler __sched, _Receiver&& __rcvr) :
+ __sched_((_Scheduler&&)__sched), __rcvr_((_Receiver&&)__rcvr),
+ __state2_(connect(schedule(__sched_), __receiver2_t{this}))
+ {}
+
+ void __complete() noexcept
+ {
+ STDEXEC_ASSERT(!__data_.valueless_by_exception());
+ std::visit(
+ [this]<class _Tup>(_Tup& __tupl) -> void {
+ if constexpr (same_as<_Tup, std::monostate>)
+ {
+ std::terminate(); // reaching this indicates a bug in
+ // schedule_from
+ }
+ else
+ {
+ __apply(
+ [&]<class... _Args>(auto __tag, _Args&... __args) -> void {
+ __tag((_Receiver&&)__rcvr_, (_Args&&)__args...);
+ },
+ __tupl);
+ }
+ },
+ __data_);
+ }
+};
+
template <class _SchedulerId, class _CvrefSenderId, class _ReceiverId>
struct __operation1
{
using _Scheduler = stdexec::__t<_SchedulerId>;
using _CvrefSender = stdexec::__cvref_t<_CvrefSenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
- using __receiver1_t =
- stdexec::__t<__receiver1<_SchedulerId, _CvrefSenderId, _ReceiverId>>;
- using __receiver2_t =
- stdexec::__t<__receiver2<_SchedulerId, _CvrefSenderId, _ReceiverId>>;
using __variant_t = __variant_for_t<_CvrefSender, env_of_t<_Receiver>>;
+ using __receiver1_t = stdexec::__t<
+ __receiver1<_SchedulerId, stdexec::__id<__variant_t>, _ReceiverId>>;
+ using __base_t = __operation1_base<_SchedulerId, stdexec::__id<__variant_t>,
+ _ReceiverId>;
- struct __t
+ struct __t : __base_t
{
using __id = __operation1;
- _Scheduler __sched_;
- _Receiver __rcvr_;
- __variant_t __data_;
connect_result_t<_CvrefSender, __receiver1_t> __state1_;
- connect_result_t<schedule_result_t<_Scheduler>, __receiver2_t>
- __state2_;
__t(_Scheduler __sched, _CvrefSender&& __sndr, _Receiver&& __rcvr) :
- __sched_((_Scheduler&&)__sched), __rcvr_((_Receiver&&)__rcvr),
- __state1_(connect((_CvrefSender&&)__sndr, __receiver1_t{this})),
- __state2_(connect(schedule(__sched_), __receiver2_t{this}))
+ __base_t{(_Scheduler&&)__sched, (_Receiver&&)__rcvr},
+ __state1_(connect((_CvrefSender&&)__sndr, __receiver1_t{this}))
{}
- STDEXEC_IMMOVABLE(__t);
-
friend void tag_invoke(start_t, __t& __op_state) noexcept
{
start(__op_state.__state1_);
}
-
- void __complete() noexcept
- {
- STDEXEC_ASSERT(!__data_.valueless_by_exception());
- std::visit(
- [&]<class _Tup>(_Tup& __tupl) -> void {
- if constexpr (same_as<_Tup, std::monostate>)
- {
- std::terminate(); // reaching this indicates a bug in
- // schedule_from
- }
- else
- {
- std::apply(
- [&]<class... _Args>(auto __tag,
- _Args&... __args) -> void {
- __tag((_Receiver&&)__rcvr_, (_Args&&)__args...);
- },
- __tupl);
- }
- },
- __data_);
- }
};
};
@@ -5670,116 +6546,131 @@
__mcompose<__q<completion_signatures>, __qf<_Tag>>>;
template <class _SchedulerId>
-struct __env
+struct __environ
{
- using _Scheduler = stdexec::__t<_SchedulerId>;
-
- struct __t
+ struct __t :
+ __env::__prop<stdexec::__t<_SchedulerId>(
+ get_completion_scheduler_t<set_value_t>,
+ get_completion_scheduler_t<set_stopped_t>)>
{
- using __id = __env;
+ using __id = __environ;
- _Scheduler __sched_;
-
- template <__one_of<set_value_t, set_stopped_t> _Tag>
- friend _Scheduler tag_invoke(get_completion_scheduler_t<_Tag>,
- const __t& __self) noexcept
+ template <same_as<get_domain_t> _Key>
+ friend auto tag_invoke(_Key, const __t& __self) noexcept
{
- return __self.__sched_;
+ return query_or(get_domain, __self.__value_, default_domain());
}
};
};
-template <class _SchedulerId, class _SenderId>
-struct __sender
+template <class... _Ts>
+using __all_nothrow_decay_copyable =
+ __mbool<(__nothrow_decay_copyable<_Ts> && ...)>;
+
+template <class _CvrefSender, class _Env>
+using __all_values_and_errors_nothrow_decay_copyable = //
+ __mand<
+ __try_error_types_of_t<_CvrefSender, _Env,
+ __q<__all_nothrow_decay_copyable>>,
+ __try_value_types_of_t<_CvrefSender, _Env,
+ __q<__all_nothrow_decay_copyable>, __q<__mand>>>;
+
+template <class _CvrefSender, class _Env>
+using __with_error_t = //
+ __if<__all_values_and_errors_nothrow_decay_copyable<_CvrefSender, _Env>,
+ completion_signatures<>, __with_exception_ptr>;
+
+template <class _Scheduler, class _CvrefSender, class _Env>
+using __completions_t = //
+ __try_make_completion_signatures<
+ _CvrefSender, _Env,
+ __try_make_completion_signatures<schedule_result_t<_Scheduler>, _Env,
+ __with_error_t<_CvrefSender, _Env>,
+ __mconst<completion_signatures<>>>,
+ __decay_signature<set_value_t>, __decay_signature<set_error_t>>;
+
+inline auto __get_env_fn() noexcept
{
- using _Scheduler = stdexec::__t<_SchedulerId>;
- using _Sender = stdexec::__t<_SenderId>;
- using _Env = stdexec::__t<__env<_SchedulerId>>;
-
- struct __t
- {
- using __id = __sender;
- using is_sender = void;
- _Env __env_;
- _Sender __sndr_;
-
- template <class _Self, class _Receiver>
- using __receiver_t = //
- stdexec::__t<
- __receiver1<_SchedulerId, stdexec::__cvref_id<_Self, _Sender>,
- stdexec::__id<_Receiver>>>;
-
- template <__decays_to<__t> _Self, receiver _Receiver>
- requires sender_to<__copy_cvref_t<_Self, _Sender>,
- __receiver_t<_Self, _Receiver>>
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr) //
- -> stdexec::__t<
- __operation1<_SchedulerId, stdexec::__cvref_id<_Self, _Sender>,
- stdexec::__id<_Receiver>>>
- {
- return {__self.__env_.__sched_, ((_Self&&)__self).__sndr_,
- (_Receiver&&)__rcvr};
- }
-
- template <class... _Ts>
- using __all_nothrow_decay_copyable =
- __mbool<(__nothrow_decay_copyable<_Ts> && ...)>;
-
- template <class _Self, class _Env>
- using __all_values_and_errors_nothrow_decay_copyable =
- __mand<error_types_of_t<__copy_cvref_t<_Self, _Sender>, _Env,
- __all_nothrow_decay_copyable>,
- value_types_of_t<__copy_cvref_t<_Self, _Sender>, _Env,
- __all_nothrow_decay_copyable, __mand>>;
-
- template <class _Self, class _Env>
- using __with_error_t =
- __if<__all_values_and_errors_nothrow_decay_copyable<_Self, _Env>,
- completion_signatures<>, __with_exception_ptr>;
-
- template <class _Self, class _Env>
- using __completions_t = //
- __try_make_completion_signatures<
- __copy_cvref_t<_Self, _Sender>, _Env,
- __try_make_completion_signatures<
- schedule_result_t<_Scheduler>, _Env,
- __with_error_t<_Self, _Env>,
- __mconst<completion_signatures<>>>,
- __decay_signature<set_value_t>, __decay_signature<set_error_t>>;
-
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> dependent_completion_signatures<_Env>;
-
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> __completions_t<_Self, _Env>
- requires true;
-
- friend const _Env& tag_invoke(get_env_t, const __t& __self) noexcept
- {
- return __self.__env_;
- }
+ return [](__ignore, const auto& __data,
+ const auto& __child) noexcept -> decltype(auto) {
+ return __join_env(__data, stdexec::get_env(__child));
};
-};
+}
struct schedule_from_t
{
template <scheduler _Scheduler, sender _Sender>
- requires tag_invocable<schedule_from_t, _Scheduler, _Sender>
auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
- noexcept(nothrow_tag_invocable<schedule_from_t, _Scheduler, _Sender>)
- -> tag_invoke_result_t<schedule_from_t, _Scheduler, _Sender>
{
- return tag_invoke(*this, (_Scheduler&&)__sched, (_Sender&&)__sndr);
+ using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>;
+ auto __env = _Env{{(_Scheduler&&)__sched}};
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<schedule_from_t>(std::move(__env), (_Sender&&)__sndr));
}
- template <scheduler _Scheduler, sender _Sender>
- auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
- -> stdexec::__t<__sender<stdexec::__id<__decay_t<_Scheduler>>,
- stdexec::__id<__decay_t<_Sender>>>>
+ using _Sender = __1;
+ using _Env = __0;
+ using __legacy_customizations_t = __types<tag_invoke_t(
+ schedule_from_t, get_completion_scheduler_t<set_value_t>(_Env&),
+ _Sender)>;
+
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+#endif
+
+ template <class _Sender>
+ using __env_t =
+ __sexpr_apply_result_t<const _Sender&, __result_of<__get_env_fn>>;
+
+ template <class _Sender>
+ using __scheduler_t =
+ __decay_t<__call_result_t<get_completion_scheduler_t<set_value_t>,
+ __env_t<_Sender>>>;
+
+ template <class _Sender, class _Receiver>
+ using __receiver_t = //
+ stdexec::__t<__receiver1<stdexec::__id<__scheduler_t<_Sender>>,
+ stdexec::__id<__variant_for_t<
+ __child_of<_Sender>, env_of_t<_Receiver>>>,
+ stdexec::__id<_Receiver>>>;
+
+ template <class _Sender, class _Receiver>
+ using __operation_t = //
+ stdexec::__t<__operation1< //
+ stdexec::__id<__scheduler_t<_Sender>>,
+ stdexec::__cvref_id<__child_of<_Sender>>,
+ stdexec::__id<_Receiver>>>;
+
+ template <sender_expr_for<schedule_from_t> _Sender>
+ static __env_t<_Sender> get_env(const _Sender& __sndr) noexcept
{
- return {{(_Scheduler&&)__sched}, (_Sender&&)__sndr};
+ return __sexpr_apply(__sndr, __get_env_fn());
+ }
+
+ template <sender_expr_for<schedule_from_t> _Sender, class _Env>
+ static auto get_completion_signatures(_Sender&&, const _Env&) noexcept
+ -> __completions_t<__scheduler_t<_Sender>, __child_of<_Sender>, _Env>
+ {
+ return {};
+ }
+
+ template <sender_expr_for<schedule_from_t> _Sender, receiver _Receiver>
+ requires sender_to<__child_of<_Sender>,
+ __receiver_t<_Sender, _Receiver>>
+ static auto connect(_Sender&& __sndr, _Receiver __rcvr) //
+ -> __operation_t<_Sender, _Receiver>
+ {
+ return __sexpr_apply((_Sender&&)__sndr,
+ [&]<class _Data, class _Child>(
+ __ignore, _Data&& __data, _Child&& __child)
+ -> __operation_t<_Sender, _Receiver> {
+ auto __sched = get_completion_scheduler<set_value_t>(__data);
+ return {__sched, (_Child&&)__child, (_Receiver&&)__rcvr};
+ });
}
};
} // namespace __schedule_from
@@ -5791,52 +6682,76 @@
// [execution.senders.adaptors.transfer]
namespace __transfer
{
+using __schedule_from::__environ;
+
+template <class _Env>
+using __scheduler_t = __result_of<get_completion_scheduler<set_value_t>, _Env>;
+
+template <class _Sender>
+using __lowered_t = //
+ __result_of<schedule_from, __scheduler_t<__data_of<_Sender>>,
+ __child_of<_Sender>>;
+
+inline auto __get_env_fn() noexcept
+{
+ return [](__ignore, const auto& __data, const auto& __child) noexcept {
+ return __join_env(__data, stdexec::get_env(__child));
+ };
+}
+
struct transfer_t
{
template <sender _Sender, scheduler _Scheduler>
- requires __tag_invocable_with_completion_scheduler<
- transfer_t, set_value_t, _Sender, _Scheduler>
- tag_invoke_result_t<transfer_t,
- __completion_scheduler_for<_Sender, set_value_t>,
- _Sender, _Scheduler>
- operator()(_Sender&& __sndr, _Scheduler&& __sched) const
- noexcept(nothrow_tag_invocable<
- transfer_t, __completion_scheduler_for<_Sender, set_value_t>,
- _Sender, _Scheduler>)
- {
- auto csch = get_completion_scheduler<set_value_t>(get_env(__sndr));
- return tag_invoke(transfer_t{}, std::move(csch), (_Sender&&)__sndr,
- (_Scheduler&&)__sched);
- }
-
- template <sender _Sender, scheduler _Scheduler>
- requires(!__tag_invocable_with_completion_scheduler<
- transfer_t, set_value_t, _Sender, _Scheduler>) &&
- tag_invocable<transfer_t, _Sender, _Scheduler>
- tag_invoke_result_t<transfer_t, _Sender, _Scheduler>
- operator()(_Sender&& __sndr, _Scheduler&& __sched) const
- noexcept(nothrow_tag_invocable<transfer_t, _Sender, _Scheduler>)
- {
- return tag_invoke(transfer_t{}, (_Sender&&)__sndr,
- (_Scheduler&&)__sched);
- }
-
- // NOT TO SPEC: permit non-typed senders:
- template <sender _Sender, scheduler _Scheduler>
- requires(!__tag_invocable_with_completion_scheduler<
- transfer_t, set_value_t, _Sender, _Scheduler>) &&
- (!tag_invocable<transfer_t, _Sender, _Scheduler>)
auto operator()(_Sender&& __sndr, _Scheduler&& __sched) const
{
- return schedule_from((_Scheduler&&)__sched, (_Sender&&)__sndr);
+ auto __domain = __get_early_domain(__sndr);
+ using _Env = __t<__environ<__id<__decay_t<_Scheduler>>>>;
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<transfer_t>(_Env{{(_Scheduler&&)__sched}},
+ (_Sender&&)__sndr));
}
template <scheduler _Scheduler>
+ STDEXEC_ATTRIBUTE((always_inline)) //
__binder_back<transfer_t, __decay_t<_Scheduler>>
operator()(_Scheduler&& __sched) const
{
return {{}, {}, {(_Scheduler&&)__sched}};
}
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ using _Env = __0;
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(transfer_t,
+ get_completion_scheduler_t<set_value_t>(
+ get_env_t(const _Sender&)),
+ _Sender,
+ get_completion_scheduler_t<set_value_t>(_Env)),
+ tag_invoke_t(transfer_t, _Sender,
+ get_completion_scheduler_t<set_value_t>(_Env))>;
+
+ template <sender_expr_for<transfer_t> _Sender>
+ static decltype(auto) get_env(const _Sender& __sndr) noexcept
+ {
+ return __sexpr_apply(__sndr, __get_env_fn());
+ }
+
+ template <class _Env>
+ static auto __transform_sender_fn(const _Env&)
+ {
+ return [&]<class _Data, class _Child>(__ignore, _Data&& __data,
+ _Child&& __child) {
+ auto __sched = get_completion_scheduler<set_value_t>(__data);
+ return schedule_from(std::move(__sched), (_Child&&)__child);
+ };
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&& __sndr, const _Env& __env)
+ {
+ return __sexpr_apply((_Sender&&)__sndr, __transform_sender_fn(__env));
+ }
};
} // namespace __transfer
@@ -5844,6 +6759,215 @@
inline constexpr transfer_t transfer{};
/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.transfer_just]
+namespace __transfer_just
+{
+// This is a helper for finding legacy cusutomizations of transfer_just.
+inline auto __transfer_just_tag_invoke()
+{
+ return []<class... _Ts>(
+ _Ts&&... __ts) -> tag_invoke_result_t<transfer_just_t, _Ts...> {
+ return tag_invoke(transfer_just, (_Ts&&)__ts...);
+ };
+}
+
+inline auto __make_env_fn() noexcept
+{
+ return []<class _Scheduler>(const _Scheduler& __sched,
+ const auto&...) noexcept {
+ using _Env = __t<__schedule_from::__environ<__id<_Scheduler>>>;
+ return _Env{{__sched}};
+ };
+}
+
+inline auto __get_env_fn() noexcept
+{
+ return [](__ignore, const auto& __data) noexcept {
+ return __apply(__make_env_fn(), __data);
+ };
+}
+
+template <class _Env>
+auto __make_transform_fn(const _Env& __env)
+{
+ return [&]<class _Scheduler, class... _Values>(_Scheduler&& __sched,
+ _Values&&... __vals) {
+ return transfer(just((_Values&&)__vals...), (_Scheduler&&)__sched);
+ };
+}
+
+template <class _Env>
+auto __transform_sender_fn(const _Env& __env)
+{
+ return [&]<class _Data>(__ignore, _Data&& __data) {
+ return __apply(__make_transform_fn(__env), (_Data&&)__data);
+ };
+}
+
+struct transfer_just_t
+{
+ using _Data = __0;
+ using __legacy_customizations_t = //
+ __types<__apply_t(decltype(__transfer_just_tag_invoke()), _Data)>;
+
+ template <scheduler _Scheduler, __movable_value... _Values>
+ auto operator()(_Scheduler&& __sched, _Values&&... __vals) const
+ {
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<transfer_just_t>(std::tuple{
+ (_Scheduler&&)__sched, (_Values&&)__vals...}));
+ }
+
+ template <sender_expr_for<transfer_just_t> _Sender>
+ static auto get_env(const _Sender& __sndr) noexcept
+ {
+ return __sexpr_apply((_Sender&&)__sndr, __get_env_fn());
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&& __sndr, const _Env& __env)
+ {
+ return __sexpr_apply((_Sender&&)__sndr, __transform_sender_fn(__env));
+ }
+};
+} // namespace __transfer_just
+
+using __transfer_just::transfer_just_t;
+inline constexpr transfer_just_t transfer_just{};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+// __write adaptor
+namespace __write_
+{
+template <class _ReceiverId, class _Env>
+struct __operation_base
+{
+ using _Receiver = __t<_ReceiverId>;
+ _Receiver __rcvr_;
+ const _Env __env_;
+};
+
+inline constexpr auto __get_env = [](auto* __op) noexcept -> decltype(auto) {
+ return (__op->__env_);
+};
+
+template <class _ReceiverId, class _Env>
+using __receiver_t = //
+ __t<__detail::__receiver_with<&__operation_base<_ReceiverId, _Env>::__rcvr_,
+ __get_env>>;
+
+template <class _CvrefSenderId, class _ReceiverId, class _Env>
+struct __operation : __operation_base<_ReceiverId, _Env>
+{
+ using _CvrefSender = __cvref_t<_CvrefSenderId>;
+ using _Receiver = __t<_ReceiverId>;
+ using __base_t = __operation_base<_ReceiverId, _Env>;
+ using __receiver_t = __write_::__receiver_t<_ReceiverId, _Env>;
+ connect_result_t<_CvrefSender, __receiver_t> __state_;
+
+ __operation(_CvrefSender&& __sndr, _Receiver&& __rcvr, auto&& __env) :
+ __base_t{(_Receiver&&)__rcvr, (decltype(__env))__env},
+ __state_{
+ stdexec::connect((_CvrefSender&&)__sndr, __receiver_t{{}, this})}
+ {}
+
+ friend void tag_invoke(start_t, __operation& __self) noexcept
+ {
+ start(__self.__state_);
+ }
+};
+
+struct __write_t : __with_default_get_env<__write_t>
+{
+ template <sender _Sender, class... _Envs>
+ auto operator()(_Sender&& __sndr, _Envs... __envs) const
+ {
+ return __make_sexpr<__write_t>(__join_env(std::move(__envs)...),
+ (_Sender&&)__sndr);
+ }
+
+ template <class... _Envs>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ auto operator()(_Envs... __envs) const -> __binder_back<__write_t, _Envs...>
+ {
+ return {{}, {}, {std::move(__envs)...}};
+ }
+
+ template <class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ static auto __transform_env_fn(_Env&& __env) noexcept
+ {
+ return [&](__ignore, const auto& __data, __ignore) noexcept {
+ return __join_env(__data, (_Env&&)__env);
+ };
+ }
+
+ template <sender_expr_for<__write_t> _Self, class _Env>
+ static auto transform_env(const _Self& __self, _Env&& __env) noexcept
+ {
+ return __sexpr_apply(__self, __transform_env_fn((_Env&&)__env));
+ }
+
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+#endif
+
+ template <class _Self, class _Receiver>
+ using __receiver_t =
+ __write_::__receiver_t<__id<_Receiver>, __decay_t<__data_of<_Self>>>;
+
+ template <class _Self, class _Receiver>
+ using __operation_t =
+ __operation<__cvref_id<__child_of<_Self>>, __id<_Receiver>,
+ __decay_t<__data_of<_Self>>>;
+
+ template <sender_expr_for<__write_t> _Self, receiver _Receiver>
+ requires sender_to<__child_of<_Self>, __receiver_t<_Self, _Receiver>>
+ static auto connect(_Self&& __self, _Receiver __rcvr)
+ -> __operation_t<_Self, _Receiver>
+ {
+ return __sexpr_apply((_Self&&)__self,
+ [&]<class _Env, class _Child>(
+ __write_t, _Env&& __env, _Child&& __child) //
+ -> __operation_t<_Self, _Receiver> {
+ return {(_Child&&)__child, (_Receiver&&)__rcvr, (_Env&&)__env};
+ });
+ }
+
+ template <sender_expr_for<__write_t> _Self, class _Env>
+ static auto get_completion_signatures(_Self&&, _Env&&)
+ -> stdexec::__completion_signatures_of_t<
+ __child_of<_Self>,
+ __env::__env_join_t<const __decay_t<__data_of<_Self>>&, _Env>>
+ {
+ return {};
+ }
+};
+} // namespace __write_
+
+using __write_::__write_t;
+inline constexpr __write_t __write{};
+
+namespace __detail
+{
+template <class _Scheduler>
+STDEXEC_ATTRIBUTE((always_inline))
+auto __mkenv_sched(_Scheduler __sched)
+{
+ auto __env = __join_env(__mkprop(__sched, get_scheduler),
+ __mkprop(get_domain));
+
+ struct __env_t : decltype(__env)
+ {};
+
+ return __env_t{__env};
+}
+} // namespace __detail
+
+/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.on]
namespace __on
{
@@ -5851,39 +6975,29 @@
struct __operation;
template <class _SchedulerId, class _SenderId, class _ReceiverId>
-struct __receiver_ref
+struct __operation_base : __immovable
{
using _Scheduler = stdexec::__t<_SchedulerId>;
using _Sender = stdexec::__t<_SenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
- struct __t : receiver_adaptor<__t>
- {
- using __id = __receiver_ref;
- stdexec::__t<__operation<_SchedulerId, _SenderId, _ReceiverId>>*
- __op_state_;
-
- _Receiver&& base() && noexcept
- {
- return (_Receiver&&)__op_state_->__rcvr_;
- }
-
- const _Receiver& base() const& noexcept
- {
- return __op_state_->__rcvr_;
- }
-
- auto get_env() const noexcept
- -> __make_env_t<env_of_t<_Receiver>,
- __with<get_scheduler_t, _Scheduler>>
- {
- return __make_env(
- stdexec::get_env(this->base()),
- __with_(get_scheduler, __op_state_->__scheduler_));
- }
- };
+ _Scheduler __scheduler_;
+ _Sender __sndr_;
+ _Receiver __rcvr_;
};
+inline constexpr auto __sched_prop = [](auto* __op) noexcept {
+ return __mkprop(__op->__scheduler_, get_scheduler);
+};
+inline constexpr auto __domain_prop = [](auto*) noexcept {
+ return __mkprop(get_domain);
+};
+
+template <class _SchedulerId, class _SenderId, class _ReceiverId>
+using __receiver_ref_t = __t<__detail::__receiver_with<
+ &__operation_base<_SchedulerId, _SenderId, _ReceiverId>::__rcvr_,
+ __sched_prop, __domain_prop>>;
+
template <class _SchedulerId, class _SenderId, class _ReceiverId>
struct __receiver
{
@@ -5895,7 +7009,7 @@
{
using __id = __receiver;
using __receiver_ref_t =
- stdexec::__t<__receiver_ref<_SchedulerId, _SenderId, _ReceiverId>>;
+ __on::__receiver_ref_t<_SchedulerId, _SenderId, _ReceiverId>;
stdexec::__t<__operation<_SchedulerId, _SenderId, _ReceiverId>>*
__op_state_;
@@ -5938,13 +7052,15 @@
using _Sender = stdexec::__t<_SenderId>;
using _Receiver = stdexec::__t<_ReceiverId>;
- struct __t
+ struct __t : __operation_base<_SchedulerId, _SenderId, _ReceiverId>
{
+ using _Base = __operation_base<_SchedulerId, _SenderId, _ReceiverId>;
using __id = __operation;
using __receiver_t =
stdexec::__t<__receiver<_SchedulerId, _SenderId, _ReceiverId>>;
using __receiver_ref_t =
- stdexec::__t<__receiver_ref<_SchedulerId, _SenderId, _ReceiverId>>;
+ __on::__receiver_ref_t<_SchedulerId, _SenderId, _ReceiverId>;
+ using __schedule_sender_t = __result_of<schedule, _Scheduler>;
friend void tag_invoke(start_t, __t& __self) noexcept
{
@@ -5953,26 +7069,28 @@
template <class _Sender2, class _Receiver2>
__t(_Scheduler __sched, _Sender2&& __sndr, _Receiver2&& __rcvr) :
- __scheduler_((_Scheduler&&)__sched), __sndr_((_Sender2&&)__sndr),
- __rcvr_((_Receiver2&&)__rcvr),
+ _Base{{},
+ (_Scheduler&&)__sched,
+ (_Sender2&&)__sndr,
+ (_Receiver2&&)__rcvr},
__data_{std::in_place_index<0>, __conv{[this] {
- return connect(schedule(__scheduler_), __receiver_t{{}, this});
+ return connect(schedule(this->__scheduler_),
+ __receiver_t{{}, this});
}}}
{}
- STDEXEC_IMMOVABLE(__t);
-
- _Scheduler __scheduler_;
- _Sender __sndr_;
- _Receiver __rcvr_;
- std::variant<
- connect_result_t<schedule_result_t<_Scheduler>, __receiver_t>,
- connect_result_t<_Sender, __receiver_ref_t>>
+ std::variant<connect_result_t<__schedule_sender_t, __receiver_t>,
+ connect_result_t<_Sender, __receiver_ref_t>>
__data_;
};
};
template <class _SchedulerId, class _SenderId>
+struct __sender;
+
+struct on_t;
+
+template <class _SchedulerId, class _SenderId>
struct __sender
{
using _Scheduler = stdexec::__t<_SchedulerId>;
@@ -5983,9 +7101,10 @@
using __id = __sender;
using is_sender = void;
+ using __schedule_sender_t = __result_of<schedule, _Scheduler>;
template <class _ReceiverId>
using __receiver_ref_t =
- stdexec::__t<__receiver_ref<_SchedulerId, _SenderId, _ReceiverId>>;
+ __on::__receiver_ref_t<_SchedulerId, _SenderId, _ReceiverId>;
template <class _ReceiverId>
using __receiver_t =
stdexec::__t<__receiver<_SchedulerId, _SenderId, _ReceiverId>>;
@@ -5999,7 +7118,7 @@
template <__decays_to<__t> _Self, receiver _Receiver>
requires constructible_from<_Sender,
__copy_cvref_t<_Self, _Sender>> &&
- sender_to<schedule_result_t<_Scheduler>,
+ sender_to<__schedule_sender_t,
__receiver_t<stdexec::__id<_Receiver>>> &&
sender_to<_Sender,
__receiver_ref_t<stdexec::__id<_Receiver>>>
@@ -6016,45 +7135,76 @@
return get_env(__self.__sndr_);
}
- template <class...>
- using __value_t = completion_signatures<>;
-
template <__decays_to<__t> _Self, class _Env>
friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
-> __try_make_completion_signatures<
- schedule_result_t<_Scheduler>, _Env,
+ __schedule_sender_t, _Env,
__try_make_completion_signatures<
__copy_cvref_t<_Self, _Sender>,
__make_env_t<_Env, __with<get_scheduler_t, _Scheduler>>,
completion_signatures<set_error_t(std::exception_ptr)>>,
- __q<__value_t>>;
+ __mconst<completion_signatures<>>>
+ {
+ return {};
+ }
+
+ // BUGBUG better would be to port the `on` algorithm to __sexpr
+ template <class _Self, class _Fun, class _OnTag = on_t>
+ static auto apply(_Self&& __self, _Fun __fun)
+ -> __call_result_t<_Fun, _OnTag, __copy_cvref_t<_Self, _Scheduler>,
+ __copy_cvref_t<_Self, _Sender>>
+ {
+ return ((_Fun&&)__fun)(_OnTag(), ((_Self&&)__self).__scheduler_,
+ ((_Self&&)__self).__sndr_);
+ }
};
};
-struct on_t
+struct on_t : __with_default_get_env<on_t>
{
- template <scheduler _Scheduler, sender _Sender>
- requires tag_invocable<on_t, _Scheduler, _Sender>
- auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
- noexcept(nothrow_tag_invocable<on_t, _Scheduler, _Sender>)
- -> tag_invoke_result_t<on_t, _Scheduler, _Sender>
- {
- return tag_invoke(*this, (_Scheduler&&)__sched, (_Sender&&)__sndr);
- }
+ using _Sender = __1;
+ using _Scheduler = __0;
+ using __legacy_customizations_t =
+ __types<tag_invoke_t(on_t, _Scheduler, _Sender)>;
template <scheduler _Scheduler, sender _Sender>
auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
- -> __t<__sender<stdexec::__id<__decay_t<_Scheduler>>,
- stdexec::__id<__decay_t<_Sender>>>>
{
- // connect-based customization will remove the need for this check
- using __has_customizations =
- __call_result_t<__has_algorithm_customizations_t, _Scheduler>;
- static_assert(
- !__has_customizations{},
- "For now the default stdexec::on implementation doesn't support scheduling "
- "onto schedulers that customize algorithms.");
- return {(_Scheduler&&)__sched, (_Sender&&)__sndr};
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<on_t>((_Scheduler&&)__sched, (_Sender&&)__sndr));
+ }
+
+ template <class _Scheduler, class _Sender>
+ using __sender_t = __t<__sender<stdexec::__id<__decay_t<_Scheduler>>,
+ stdexec::__id<__decay_t<_Sender>>>>;
+
+ template <class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ static auto __transform_env_fn(_Env&& __env) noexcept
+ {
+ return [&](__ignore, auto __sched, __ignore) noexcept {
+ return __join_env(__detail::__mkenv_sched(__sched), (_Env&&)__env);
+ };
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_env(const _Sender& __sndr, _Env&& __env) noexcept
+ {
+ return __sexpr_apply(__sndr, __transform_env_fn((_Env&&)__env));
+ }
+
+ template <class _Sender, class _Env>
+ requires __is_not_instance_of<__id<__decay_t<_Sender>>, __sender>
+ static auto transform_sender(_Sender&& __sndr, const _Env&)
+ {
+ return __sexpr_apply((_Sender&&)__sndr,
+ []<class _Data, class _Child>(
+ __ignore, _Data&& __data, _Child&& __child) {
+ return __sender_t<_Data, _Child>{(_Data&&)__data,
+ (_Child&&)__child};
+ });
}
};
} // namespace __on
@@ -6062,37 +7212,13 @@
using __on::on_t;
inline constexpr on_t on{};
-/////////////////////////////////////////////////////////////////////////////
-// [execution.senders.transfer_just]
-namespace __transfer_just
+// BUGBUG this will also be unnecessary when `on` returns a __sexpr
+namespace __detail
{
-struct transfer_just_t
-{
- template <scheduler _Scheduler, __movable_value... _Values>
- requires tag_invocable<transfer_just_t, _Scheduler, _Values...> &&
- sender<tag_invoke_result_t<transfer_just_t, _Scheduler,
- _Values...>>
- auto operator()(_Scheduler&& __sched, _Values&&... __vals) const
- noexcept(nothrow_tag_invocable<transfer_just_t, _Scheduler, _Values...>)
- -> tag_invoke_result_t<transfer_just_t, _Scheduler, _Values...>
- {
- return tag_invoke(*this, (_Scheduler&&)__sched, (_Values&&)__vals...);
- }
-
- template <scheduler _Scheduler, __movable_value... _Values>
- requires(!tag_invocable<transfer_just_t, _Scheduler, _Values...> ||
- !sender<tag_invoke_result_t<transfer_just_t, _Scheduler,
- _Values...>>)
- auto operator()(_Scheduler&& __sched, _Values&&... __vals) const
- -> decltype(transfer(just((_Values&&)__vals...), (_Scheduler&&)__sched))
- {
- return transfer(just((_Values&&)__vals...), (_Scheduler&&)__sched);
- }
-};
-} // namespace __transfer_just
-
-using __transfer_just::transfer_just_t;
-inline constexpr transfer_just_t transfer_just{};
+template <class _SchedulerId, class _SenderId>
+extern __mconst<__on::__sender<__t<_SchedulerId>, __name_of<__t<_SenderId>>>>
+ __name_of_v<__on::__sender<_SchedulerId, _SenderId>>;
+}
/////////////////////////////////////////////////////////////////////////////
// [execution.senders.adaptors.into_variant]
@@ -6152,79 +7278,72 @@
};
};
-template <class _SenderId>
-struct __sender
-{
- using _Sender = stdexec::__t<_SenderId>;
+template <class _Sender, class _Env>
+using __variant_t = __try_value_types_of_t<_Sender, _Env>;
- template <class _Env>
- using __variant_t = __into_variant_result_t<_Sender, _Env>;
+template <class _Sender, class _Receiver>
+using __receiver_t = //
+ stdexec::__t<
+ __receiver<__id<_Receiver>, __variant_t<_Sender, env_of_t<_Receiver>>>>;
- template <class _Receiver>
- using __receiver_t = //
- stdexec::__t<
- __receiver<__id<_Receiver>, __variant_t<env_of_t<_Receiver>>>>;
+template <class _Variant>
+using __variant_completions =
+ completion_signatures<set_value_t(_Variant),
+ set_error_t(std::exception_ptr)>;
- struct __t
- {
- using __id = __sender;
- using is_sender = void;
+template <class _Sender, class _Env>
+using __compl_sigs = //
+ __try_make_completion_signatures<
+ _Sender, _Env,
+ __meval<__variant_completions, __variant_t<_Sender, _Env>>,
+ __mconst<completion_signatures<>>>;
- template <__decays_to<_Sender> _CvrefSender>
- explicit __t(_CvrefSender&& __sndr) : __sndr_((_CvrefSender&&)__sndr)
- {}
-
- private:
- template <class...>
- using __value_t = completion_signatures<>;
-
- template <class _Env>
- using __compl_sigs = //
- make_completion_signatures<
- _Sender, _Env,
- completion_signatures<set_value_t(__variant_t<_Env>),
- set_error_t(std::exception_ptr)>,
- __value_t>;
-
- _Sender __sndr_;
-
- template <receiver _Receiver>
- requires sender_to<_Sender, __receiver_t<_Receiver>>
- friend auto tag_invoke(connect_t, __t&& __self, _Receiver __rcvr) //
- noexcept(__nothrow_connectable<_Sender, __receiver_t<_Receiver>>)
- -> connect_result_t<_Sender, __receiver_t<_Receiver>>
- {
- return stdexec::connect(
- (_Sender&&)__self.__sndr_,
- __receiver_t<_Receiver>{(_Receiver&&)__rcvr});
- }
-
- friend auto tag_invoke(get_env_t, const __t& __self) noexcept
- -> env_of_t<const _Sender&>
- {
- return get_env(__self.__sndr_);
- }
-
- template <class _Env>
- friend auto tag_invoke(get_completion_signatures_t, __t&&, _Env&&) //
- -> __compl_sigs<_Env>;
- };
-};
-
-struct into_variant_t
+struct into_variant_t : __with_default_get_env<into_variant_t>
{
template <sender _Sender>
auto operator()(_Sender&& __sndr) const
- -> __t<__sender<stdexec::__id<__decay_t<_Sender>>>>
{
- return __t<__sender<stdexec::__id<__decay_t<_Sender>>>>{
- (_Sender&&)__sndr};
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<into_variant_t>(__(), std::move(__sndr)));
}
+ STDEXEC_ATTRIBUTE((always_inline)) //
auto operator()() const noexcept
{
return __binder_back<into_variant_t>{};
}
+
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+#endif
+
+ template <sender_expr_for<into_variant_t> _Self, receiver _Receiver>
+ requires sender_to<__child_of<_Self>,
+ __receiver_t<__child_of<_Self>, _Receiver>>
+ static auto connect(_Self&& __self, _Receiver __rcvr) //
+ noexcept(__nothrow_connectable<
+ __child_of<_Self>, __receiver_t<__child_of<_Self>, _Receiver>>)
+ -> connect_result_t<__child_of<_Self>,
+ __receiver_t<__child_of<_Self>, _Receiver>>
+ {
+ return __sexpr_apply(
+ (_Self&&)__self,
+ [&]<class _Child>(__ignore, __ignore, _Child&& __child) {
+ return stdexec::connect(
+ (_Child&&)__child,
+ __receiver_t<_Child, _Receiver>{(_Receiver&&)__rcvr});
+ });
+ }
+
+ template <sender_expr_for<into_variant_t> _Self, class _Env>
+ static auto get_completion_signatures(_Self&&, _Env&&) //
+ -> __compl_sigs<__child_of<_Self>, _Env>
+ {
+ return {};
+ }
};
} // namespace __into_variant
@@ -6256,10 +7375,10 @@
template <class _Env>
auto __make_env(_Env&& __env, in_place_stop_source& __stop_source) noexcept
{
- return __env::__join_env(__env::__env_fn{[&](get_stop_token_t) noexcept {
+ return __join_env(__env::__env_fn{[&](get_stop_token_t) noexcept {
return __stop_source.get_token();
}},
- (_Env&&)__env);
+ (_Env&&)__env);
}
template <class _Env>
@@ -6272,14 +7391,32 @@
template <class _Sender, class _Env>
concept __max1_sender = sender_in<_Sender, _Env> &&
- __valid<__value_types_of_t, _Sender, _Env,
- __mconst<int>, __msingle_or<void>>;
+ __mvalid<__value_types_of_t, _Sender, _Env,
+ __mconst<int>, __msingle_or<void>>;
+
+template <
+ __mstring _Context = "In stdexec::when_all()..."__csz,
+ __mstring _Diagnostic =
+ "The given sender can complete successfully in more that one way. "
+ "Use stdexec::when_all_with_variant() instead."__csz>
+struct _INVALID_WHEN_ALL_ARGUMENT_;
+
+template <class _Sender, class _Env>
+using __too_many_value_completions_error =
+ __mexception<_INVALID_WHEN_ALL_ARGUMENT_<>, _WITH_SENDER_<_Sender>,
+ _WITH_ENVIRONMENT_<_Env>>;
+
+template <class _Sender, class _Env, class _ValueTuple, class... _Rest>
+using __value_tuple_t =
+ __minvoke<__if_c<(0 == sizeof...(_Rest)), __mconst<_ValueTuple>,
+ __q<__too_many_value_completions_error>>,
+ _Sender, _Env>;
template <class _Env, class _Sender>
using __single_values_of_t = //
__try_value_types_of_t<_Sender, _Env,
__transform<__q<__decay_rvalue_ref>, __q<__types>>,
- __q<__msingle>>;
+ __mbind_front_q<__value_tuple_t, _Sender, _Env>>;
template <class _Env, class... _Senders>
using __set_values_sig_t = //
@@ -6291,20 +7428,17 @@
using __all_nothrow_decay_copyable =
__mbool<(__nothrow_decay_copyable<_Args> && ...)>;
-template <class _Env, class... _SenderIds>
+template <class _Env, class... _Senders>
using __all_value_and_error_args_nothrow_decay_copyable = //
- __mand< //
- __mand<__try_value_types_of_t<__t<_SenderIds>, _Env,
- __q<__all_nothrow_decay_copyable>,
- __q<__mand>>...>,
- __mand<__try_error_types_of_t<__t<_SenderIds>, _Env,
- __q<__all_nothrow_decay_copyable>>...>>;
+ __mand<__compl_sigs::__maybe_for_all_sigs<
+ __completion_signatures_of_t<_Senders, _Env>,
+ __q<__all_nothrow_decay_copyable>, __q<__mand>>...>;
template <class _Env, class... _Senders>
using __completions_t = //
__concat_completion_signatures_t<
- __if<__all_value_and_error_args_nothrow_decay_copyable<
- _Env, __id<_Senders>...>,
+ __if<__all_value_and_error_args_nothrow_decay_copyable<_Env,
+ _Senders...>,
completion_signatures<set_stopped_t()>,
completion_signatures<set_stopped_t(),
set_error_t(std::exception_ptr&&)>>,
@@ -6354,10 +7488,10 @@
template <class _Receiver, class _ValuesTuple>
void __set_values(_Receiver& __rcvr, _ValuesTuple& __values) noexcept
{
- std::apply(
+ __apply(
[&](auto&... __opt_vals) noexcept -> void {
- std::apply(__complete_fn{set_value, __rcvr},
- std::tuple_cat(std::apply(__tie_fn{}, *__opt_vals)...));
+ __apply(__complete_fn{set_value, __rcvr},
+ std::tuple_cat(__apply(__tie_fn{}, *__opt_vals)...));
},
__values);
}
@@ -6410,7 +7544,7 @@
// Could be non-atomic here and atomic_ref everywhere except __completion_fn
std::atomic<__state_t> __state_{__started};
_ErrorsVariant __errors_{};
- STDEXEC_NO_UNIQUE_ADDRESS _ValuesTuple __values_{};
+ STDEXEC_ATTRIBUTE((no_unique_address)) _ValuesTuple __values_{};
std::optional<typename stop_token_of_t<
env_of_t<_Receiver>&>::template callback_type<__on_stop_requested>>
__on_stop_{};
@@ -6548,8 +7682,6 @@
template <class _Env, __max1_sender<__env_t<_Env>>... _Senders>
struct __traits_
{
- using __completions = __completions_t<__env_t<_Env>, _Senders...>;
-
// tuple<optional<tuple<Vs1...>>, optional<tuple<Vs2...>>, ...>
using __values_tuple = //
__minvoke<__with_default<
@@ -6566,8 +7698,8 @@
error_types_of_t<_Senders, __env_t<_Env>, __types>...>;
using __errors_variant = //
- __if<__all_value_and_error_args_nothrow_decay_copyable<
- _Env, __id<_Senders>...>,
+ __if<__all_value_and_error_args_nothrow_decay_copyable<_Env,
+ _Senders...>,
__error_types,
__minvoke<__push_back_unique<__q<std::variant>>, __error_types,
std::exception_ptr>>;
@@ -6578,7 +7710,6 @@
struct __traits : __traits_<env_of_t<_Receiver>, _Senders...>
{
using _Traits = __traits_<env_of_t<_Receiver>, _Senders...>;
- using typename _Traits::__completions;
using typename _Traits::__errors_variant;
using typename _Traits::__values_tuple;
@@ -6610,12 +7741,11 @@
_SenderIds...>::template __op_states_tuple<>;
template <class _Cvref, class _ReceiverId, class... _SenderIds>
- requires __valid<__op_states_tuple_ex, _Cvref, _ReceiverId, _SenderIds...>
+ requires __mvalid<__op_states_tuple_ex, _Cvref, _ReceiverId, _SenderIds...>
struct __operation
{
using _Receiver = stdexec::__t<_ReceiverId>;
using _Traits = __traits_ex<_Cvref, _ReceiverId, _SenderIds...>;
- using _Indices = std::index_sequence_for<_SenderIds...>;
using __operation_base_t = typename _Traits::__operation_base;
using __op_states_tuple_t =
@@ -6628,21 +7758,15 @@
{
using __id = __operation;
- template <class _SendersTuple, std::size_t... _Is>
- __t(_SendersTuple&& __sndrs, _Receiver __rcvr,
- std::index_sequence<_Is...>) :
+ template <std::size_t... _Is, class... _Senders>
+ __t(_Receiver __rcvr, __indices<_Is...>, _Senders&&... __sndrs) :
__operation_base_t{{}, (_Receiver&&)__rcvr, {sizeof...(_Is)}},
- __op_states_{__conv{[&__sndrs, this]() {
- return stdexec::connect(std::get<_Is>((_SendersTuple&&)__sndrs),
+ __op_states_{__conv{[&, this]() {
+ return stdexec::connect((_Senders&&)__sndrs,
__receiver_t<_Is>{this});
}}...}
{}
- template <class _SendersTuple>
- __t(_SendersTuple&& __sndrs, _Receiver __rcvr) :
- __t((_SendersTuple&&)__sndrs, (_Receiver&&)__rcvr, _Indices{})
- {}
-
friend void tag_invoke(start_t, __t& __self) noexcept
{
// register stop callback:
@@ -6657,7 +7781,7 @@
}
else
{
- std::apply(
+ __apply(
[](auto&... __child_ops) noexcept -> void {
(stdexec::start(__child_ops), ...);
},
@@ -6673,176 +7797,221 @@
};
};
-template <class _From, class _ToId>
-using __cvref_id = __copy_cvref_t<_From, __t<_ToId>>;
+struct _INVALID_ARGUMENTS_TO_WHEN_ALL_
+{};
-template <class _Indices, class... _SenderIds>
-struct __sender;
-
-template <std::size_t... _Indices, class... _SenderIds>
-struct __sender<std::index_sequence<_Indices...>, _SenderIds...>
+struct when_all_t : __domain::__get_env_common_domain<when_all_t>
{
- template <class _Self, class _Env>
- using __completions_t =
- typename __traits_<_Env,
- __cvref_id<_Self, _SenderIds>...>::__completions;
+ // Used by the default_domain to find legacy customizations:
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(when_all_t, _Sender...)>;
- template <class _Self, class _Receiver, std::size_t _Index>
- using __receiver_t =
- typename __traits<_Receiver, __cvref_id<_Self, _SenderIds>...>::
- template __receiver<_Index>;
+ // TODO: improve diagnostic when senders have different domains
+ template <sender... _Senders>
+ requires __domain::__has_common_domain<_Senders...>
+ auto operator()(_Senders&&... __sndrs) const
+ {
+ auto __domain = __domain::__common_domain_t<_Senders...>();
+ return stdexec::transform_sender(
+ __domain, __make_sexpr<when_all_t>(__(), (_Senders&&)__sndrs...));
+ }
+
+#if STDEXEC_FRIENDSHIP_IS_LEXICAL()
+ private:
+ template <class...>
+ friend struct stdexec::__sexpr;
+#endif
+
+ template <class _Self, class _Env>
+ using __error = __mexception<_INVALID_ARGUMENTS_TO_WHEN_ALL_,
+ __children_of<_Self, __q<_WITH_SENDERS_>>,
+ _WITH_ENVIRONMENT_<_Env>>;
+
+ template <class _Self, class _Env>
+ using __completions = //
+ __children_of<_Self, __mbind_front_q<__completions_t, __env_t<_Env>>>;
+
+ template <sender_expr_for<when_all_t> _Self, class _Env>
+ static auto get_completion_signatures(_Self&&, _Env&&)
+ {
+ return __minvoke<__mtry_catch<__q<__completions>, __q<__error>>, _Self,
+ _Env>();
+ }
+
+ template <class _Receiver, class _Indices>
+ struct __connect_fn;
+
+ template <class _Receiver, std::size_t... _Indices>
+ struct __connect_fn<_Receiver, __indices<_Indices...>>
+ {
+ _Receiver* __rcvr_;
+
+ template <std::size_t _Index, class... _Senders>
+ using __receiver_t =
+ typename __traits<_Receiver,
+ _Senders...>::template __receiver<_Index>;
+
+ template <class... _Senders>
+ requires(sender_to<_Senders, __receiver_t<_Indices, _Senders...>> &&
+ ...)
+ auto operator()(__ignore, __ignore, _Senders&&... __sndrs) const
+ {
+ using _Cvref = __copy_cvref_fn<__mfront<_Senders..., int>>;
+ using __operation_t =
+ __t<__operation<_Cvref, __id<_Receiver>,
+ __id<__decay_t<_Senders>>...>>;
+ return __operation_t{(_Receiver&&)*__rcvr_,
+ __indices<_Indices...>(),
+ (_Senders&&)__sndrs...};
+ }
+ };
template <class _Self, class _Receiver>
- using __operation_t =
- stdexec::__t<__operation<__copy_cvref_fn<_Self>,
- stdexec::__id<_Receiver>, _SenderIds...>>;
+ using __connect_fn_for = //
+ __connect_fn<_Receiver, __make_indices<__nbr_children_of<_Self>>>;
- struct __t
+ template <sender_expr_for<when_all_t> _Self, class _Receiver>
+ requires __callable<__sexpr_apply_t, _Self,
+ __connect_fn_for<_Self, _Receiver>>
+ static auto connect(_Self&& __self, _Receiver __rcvr)
{
- using __id = __sender;
- using is_sender = void;
-
- template <class... _Sndrs>
- explicit(sizeof...(_Sndrs) == 1) __t(_Sndrs&&... __sndrs) :
- __sndrs_((_Sndrs&&)__sndrs...)
- {}
-
- private:
- template <__decays_to<__t> _Self, receiver _Receiver>
- requires(sender_to<__cvref_id<_Self, _SenderIds>,
- __receiver_t<_Self, _Receiver, _Indices>> &&
- ...)
- friend auto tag_invoke(connect_t, _Self&& __self, _Receiver __rcvr)
- -> __operation_t<_Self, _Receiver>
- {
- return {((_Self&&)__self).__sndrs_, (_Receiver&&)__rcvr};
- }
-
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> dependent_completion_signatures<_Env>;
- template <__decays_to<__t> _Self, class _Env>
- friend auto tag_invoke(get_completion_signatures_t, _Self&&, _Env&&)
- -> __completions_t<_Self, _Env>
- requires true;
-
- friend empty_env tag_invoke(get_env_t, const __t&) noexcept
- {
- return {};
- }
-
- std::tuple<stdexec::__t<_SenderIds>...> __sndrs_;
- };
-};
-
-template <class _Sender>
-using __into_variant_result_t = decltype(into_variant(__declval<_Sender>()));
-
-struct when_all_t
-{
- template <class... _Senders>
- using __sender_t = __t<__sender<std::index_sequence_for<_Senders...>,
- __id<__decay_t<_Senders>>...>>;
-
- template <sender... _Senders>
- requires tag_invocable<when_all_t, _Senders...> &&
- sender<tag_invoke_result_t<when_all_t, _Senders...>>
- auto operator()(_Senders&&... __sndrs) const
- noexcept(nothrow_tag_invocable<when_all_t, _Senders...>)
- -> tag_invoke_result_t<when_all_t, _Senders...>
- {
- return tag_invoke(*this, (_Senders&&)__sndrs...);
- }
-
- template <sender... _Senders>
- requires(!tag_invocable<when_all_t, _Senders...>) &&
- sender<__sender_t<_Senders...>>
- __sender_t<_Senders...> operator()(_Senders&&... __sndrs) const
- {
- return __sender_t<_Senders...>{(_Senders&&)__sndrs...};
+ using __connect_fn = __connect_fn_for<_Self, _Receiver>;
+ return __sexpr_apply((_Self&&)__self, __connect_fn{&__rcvr});
}
};
-struct when_all_with_variant_t
+struct when_all_with_variant_t :
+ __domain::__get_env_common_domain<when_all_with_variant_t>
{
- template <sender... _Senders>
- requires tag_invocable<when_all_with_variant_t, _Senders...> &&
- sender<
- tag_invoke_result_t<when_all_with_variant_t, _Senders...>>
- auto operator()(_Senders&&... __sndrs) const
- noexcept(nothrow_tag_invocable<when_all_with_variant_t, _Senders...>)
- -> tag_invoke_result_t<when_all_with_variant_t, _Senders...>
- {
- return tag_invoke(*this, (_Senders&&)__sndrs...);
- }
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(when_all_with_variant_t, _Sender...)>;
template <sender... _Senders>
- requires(!tag_invocable<when_all_with_variant_t, _Senders...>) &&
- (__callable<into_variant_t, _Senders> && ...)
+ requires __domain::__has_common_domain<_Senders...>
auto operator()(_Senders&&... __sndrs) const
{
- return when_all_t{}(into_variant((_Senders&&)__sndrs)...);
+ auto __domain = __domain::__common_domain_t<_Senders...>();
+ return stdexec::transform_sender(__domain,
+ __make_sexpr<when_all_with_variant_t>(
+ __(), (_Senders&&)__sndrs...));
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&& __sndr, const _Env& __env)
+ {
+ // transform the when_all_with_variant into a regular when_all (looking
+ // for early when_all customizations), then transform it again to look
+ // for late customizations.
+ return __sexpr_apply(
+ (_Sender&&)__sndr,
+ [&]<class... _Child>(__ignore, __ignore, _Child&&... __child) {
+ return when_all_t()(into_variant((_Child&&)__child)...);
+ });
}
};
struct transfer_when_all_t
{
- template <scheduler _Sched, sender... _Senders>
- requires tag_invocable<transfer_when_all_t, _Sched, _Senders...> &&
- sender<tag_invoke_result_t<transfer_when_all_t, _Sched,
- _Senders...>>
- auto operator()(_Sched&& __sched, _Senders&&... __sndrs) const noexcept(
- nothrow_tag_invocable<transfer_when_all_t, _Sched, _Senders...>)
- -> tag_invoke_result_t<transfer_when_all_t, _Sched, _Senders...>
+ using _Env = __0;
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(
+ transfer_when_all_t,
+ get_completion_scheduler_t<set_value_t>(const _Env&), _Sender...)>;
+
+ template <scheduler _Scheduler, sender... _Senders>
+ requires __domain::__has_common_domain<_Senders...>
+ auto operator()(_Scheduler&& __sched, _Senders&&... __sndrs) const
{
- return tag_invoke(*this, (_Sched&&)__sched, (_Senders&&)__sndrs...);
+ using _Env =
+ __t<__schedule_from::__environ<__id<__decay_t<_Scheduler>>>>;
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<transfer_when_all_t>(_Env{{(_Scheduler&&)__sched}},
+ (_Senders&&)__sndrs...));
}
- template <scheduler _Sched, sender... _Senders>
- requires((!tag_invocable<transfer_when_all_t, _Sched, _Senders...>) ||
- (!sender<tag_invoke_result_t<transfer_when_all_t, _Sched,
- _Senders...>>))
- auto operator()(_Sched&& __sched, _Senders&&... __sndrs) const
+ template <sender_expr_for<transfer_when_all_t> _Sender>
+ static __data_of<const _Sender&> get_env(const _Sender& __self) noexcept
{
- return transfer(when_all_t{}((_Senders&&)__sndrs...),
- (_Sched&&)__sched);
+ return __sexpr_apply(__self, __detail::__get_data());
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&& __sndr, const _Env& __env)
+ {
+ // transform the transfer_when_all into a regular transform | when_all
+ // (looking for early customizations), then transform it again to look
+ // for late customizations.
+ return __sexpr_apply(
+ (_Sender&&)__sndr,
+ [&]<class _Data, class... _Child>(__ignore, _Data&& __data,
+ _Child&&... __child) {
+ return transfer(when_all_t()((_Child&&)__child...),
+ get_completion_scheduler<set_value_t>(__data));
+ });
}
};
struct transfer_when_all_with_variant_t
{
- template <scheduler _Sched, sender... _Senders>
- requires tag_invocable<transfer_when_all_with_variant_t, _Sched,
- _Senders...> &&
- sender<tag_invoke_result_t<transfer_when_all_with_variant_t,
- _Sched, _Senders...>>
- auto operator()(_Sched&& __sched, _Senders&&... __sndrs) const
- noexcept(nothrow_tag_invocable<transfer_when_all_with_variant_t, _Sched,
- _Senders...>)
- -> tag_invoke_result_t<transfer_when_all_with_variant_t, _Sched,
- _Senders...>
+ using _Env = __0;
+ using _Sender = __1;
+ using __legacy_customizations_t = //
+ __types<tag_invoke_t(
+ transfer_when_all_with_variant_t,
+ get_completion_scheduler_t<set_value_t>(const _Env&), _Sender...)>;
+
+ template <scheduler _Scheduler, sender... _Senders>
+ requires __domain::__has_common_domain<_Senders...>
+ auto operator()(_Scheduler&& __sched, _Senders&&... __sndrs) const
{
- return tag_invoke(*this, (_Sched&&)__sched, (_Senders&&)__sndrs...);
+ using _Env =
+ __t<__schedule_from::__environ<__id<__decay_t<_Scheduler>>>>;
+ auto __domain = query_or(get_domain, __sched, default_domain());
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<transfer_when_all_with_variant_t>(
+ _Env{{(_Scheduler&&)__sched}}, (_Senders&&)__sndrs...));
}
- template <scheduler _Sched, sender... _Senders>
- requires(!tag_invocable<transfer_when_all_with_variant_t, _Sched,
- _Senders...>) &&
- (__callable<into_variant_t, _Senders> && ...)
- auto operator()(_Sched&& __sched, _Senders&&... __sndrs) const
+ template <sender_expr_for<transfer_when_all_with_variant_t> _Sender>
+ static __data_of<const _Sender&> get_env(const _Sender& __self) noexcept
{
- return transfer_when_all_t{}((_Sched&&)__sched,
- into_variant((_Senders&&)__sndrs)...);
+ return __sexpr_apply(__self, __detail::__get_data());
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&& __sndr, const _Env& __env)
+ {
+ // transform the transfer_when_allwith_variant into regular
+ // transform_when_all and into_variant calls/ (looking for early
+ // customizations), then transform it again to look for late
+ // customizations.
+ return __sexpr_apply(
+ (_Sender&&)__sndr,
+ [&]<class _Data, class... _Child>(__ignore, _Data&& __data,
+ _Child&&... __child) {
+ return transfer_when_all_t()(
+ get_completion_scheduler<set_value_t>((_Data&&)__data),
+ into_variant((_Child&&)__child)...);
+ });
}
};
} // namespace __when_all
using __when_all::when_all_t;
inline constexpr when_all_t when_all{};
+
using __when_all::when_all_with_variant_t;
inline constexpr when_all_with_variant_t when_all_with_variant{};
+
using __when_all::transfer_when_all_t;
inline constexpr transfer_when_all_t transfer_when_all{};
+
using __when_all::transfer_when_all_with_variant_t;
inline constexpr transfer_when_all_with_variant_t
transfer_when_all_with_variant{};
@@ -6850,6 +8019,15 @@
namespace __read
{
template <class _Tag, class _ReceiverId>
+using __result_t = __call_result_t<_Tag, env_of_t<stdexec::__t<_ReceiverId>>>;
+
+template <class _Tag, class _ReceiverId>
+concept __nothrow_t =
+ __nothrow_callable<_Tag, env_of_t<stdexec::__t<_ReceiverId>>>;
+
+// NOT TO SPEC: if the query returns a value, store the value in the operation
+// state and pass an rvalue reference to it.
+template <class _Tag, class _ReceiverId>
struct __operation
{
using _Receiver = stdexec::__t<_ReceiverId>;
@@ -6858,22 +8036,66 @@
{
using __id = __operation;
_Receiver __rcvr_;
+ std::optional<__result_t<_Tag, _ReceiverId>> __result_;
+
+ friend void tag_invoke(start_t, __t& __self) noexcept
+ {
+ constexpr bool _Nothrow =
+ __nothrow_callable<_Tag, env_of_t<_Receiver>>;
+ auto __query =
+ [&]() noexcept(_Nothrow) -> __result_t<_Tag, _ReceiverId>&& {
+ __self.__result_.emplace(__conv{[&]() noexcept(_Nothrow) {
+ return _Tag()(get_env(__self.__rcvr_));
+ }});
+ return std::move(*__self.__result_);
+ };
+ stdexec::__set_value_invoke(std::move(__self.__rcvr_), __query);
+ }
+ };
+};
+
+// if the query returns a reference type, pass it straight through to the
+// receiver.
+template <class _Tag, class _ReceiverId>
+ requires std::same_as<__result_t<_Tag, _ReceiverId>&&,
+ __result_t<_Tag, _ReceiverId>>
+struct __operation<_Tag, _ReceiverId>
+{
+ using _Receiver = stdexec::__t<_ReceiverId>;
+
+ struct __t : __immovable
+ {
+ using __id = __operation;
+ _Receiver __rcvr_;
friend void tag_invoke(start_t, __t& __self) noexcept
{
- try
- {
- auto __env = get_env(__self.__rcvr_);
- set_value(std::move(__self.__rcvr_), _Tag{}(__env));
- }
- catch (...)
- {
- set_error(std::move(__self.__rcvr_), std::current_exception());
- }
+ stdexec::__set_value_invoke(std::move(__self.__rcvr_), _Tag(),
+ get_env(__self.__rcvr_));
}
};
};
+inline constexpr __mstring __query_failed_diag =
+ "The current execution environment doesn't have a value for the given query."__csz;
+
+template <class _Tag>
+struct _WITH_QUERY_;
+
+template <class _Tag, class _Env>
+using __query_failed_error = //
+ __mexception< //
+ _NOT_CALLABLE_<"In stdexec::read()..."__csz, __query_failed_diag>,
+ _WITH_QUERY_<_Tag>, _WITH_ENVIRONMENT_<_Env>>;
+
+template <class _Tag, class _Env>
+ requires __callable<_Tag, _Env>
+using __completions_t = //
+ __if_c<__nothrow_callable<_Tag, _Env>,
+ completion_signatures<set_value_t(__call_result_t<_Tag, _Env>)>,
+ completion_signatures<set_value_t(__call_result_t<_Tag, _Env>),
+ set_error_t(std::exception_ptr)>>;
+
template <class _Tag>
struct __sender
{
@@ -6882,10 +8104,9 @@
using is_sender = void;
template <class _Env>
- requires __callable<_Tag, _Env>
- using __completions_t = //
- completion_signatures<set_value_t(__call_result_t<_Tag, _Env>),
- set_error_t(std::exception_ptr)>;
+ using __completions_t = __minvoke<
+ __mtry_catch_q<__read::__completions_t, __q<__query_failed_error>>,
+ _Tag, _Env>;
template <class _Receiver>
requires receiver_of<_Receiver, __completions_t<env_of_t<_Receiver>>>
@@ -6898,10 +8119,10 @@
template <class _Env>
friend auto tag_invoke(get_completion_signatures_t, __sender, _Env&&)
- -> dependent_completion_signatures<_Env>;
- template <__none_of<no_env> _Env>
- friend auto tag_invoke(get_completion_signatures_t, __sender, _Env&&)
- -> __completions_t<_Env>;
+ -> __completions_t<_Env>
+ {
+ return {};
+ }
friend empty_env tag_invoke(get_env_t, const __t&) noexcept
{
@@ -6983,13 +8204,179 @@
} // namespace __queries
/////////////////////////////////////////////////////////////////////////////
+// [execution.senders.adaptors.on]
+namespace __on_v2
+{
+inline constexpr __mstring __on_context =
+ "In stdexec::on(Scheduler, Sender)..."__csz;
+inline constexpr __mstring __no_scheduler_diag =
+ "stdexec::on() requires a scheduler to transition back to."__csz;
+inline constexpr __mstring __no_scheduler_details =
+ "The provided environment lacks a value for the get_scheduler() query."__csz;
+
+template <__mstring _Context = __on_context,
+ __mstring _Diagnostic = __no_scheduler_diag,
+ __mstring _Details = __no_scheduler_details>
+struct _CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_
+{};
+
+struct on_t;
+
+STDEXEC_PRAGMA_PUSH()
+STDEXEC_PRAGMA_IGNORE_GNU("-Wunused-local-typedefs")
+
+struct __no_scheduler_in_environment
+{
+ // Issue a custom diagnostic if the environment doesn't provide a scheduler.
+ template <class _Sender, class _Env>
+ static auto transform_sender(_Sender&&, const _Env&)
+ {
+ struct __no_scheduler_in_environment
+ {
+ using is_sender = void;
+ using completion_signatures = //
+ __mexception<_CANNOT_RESTORE_EXECUTION_CONTEXT_AFTER_ON_<>,
+ _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
+ };
+
+ return __no_scheduler_in_environment{};
+ }
+};
+
+STDEXEC_PRAGMA_POP()
+
+template <class _Ty, class = __name_of<__decay_t<_Ty>>>
+struct __always
+{
+ _Ty __val_;
+
+ _Ty operator()() noexcept
+ {
+ return static_cast<_Ty&&>(__val_);
+ }
+};
+
+template <class _Ty>
+__always(_Ty) -> __always<_Ty>;
+
+struct on_t : __with_default_get_env<on_t>, __no_scheduler_in_environment
+{
+ template <scheduler _Scheduler, sender _Sender>
+ auto operator()(_Scheduler&& __sched, _Sender&& __sndr) const
+ {
+ // BUGBUG __get_early_domain, or get_domain(__sched), or ...?
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<on_t>((_Scheduler&&)__sched, (_Sender&&)__sndr));
+ }
+
+ template <class _Env>
+ STDEXEC_ATTRIBUTE((always_inline))
+ static auto __transform_env_fn(_Env&& __env) noexcept
+ {
+ return [&](__ignore, auto __sched, __ignore) noexcept {
+ return __join_env(__detail::__mkenv_sched(__sched), (_Env&&)__env);
+ };
+ }
+
+ template <class _Sender, class _Env>
+ static auto transform_env(const _Sender& __sndr, _Env&& __env) noexcept
+ {
+ return __sexpr_apply(__sndr, __transform_env_fn((_Env&&)__env));
+ }
+
+ using __no_scheduler_in_environment::transform_sender;
+
+ template <class _Sender, class _Env>
+ requires __callable<get_scheduler_t, const _Env&>
+ static auto transform_sender(_Sender&& __sndr, const _Env& __env)
+ {
+ return __sexpr_apply(
+ (_Sender&&)__sndr,
+ [&]<class _Scheduler, class _Child>(__ignore, _Scheduler __sched,
+ _Child&& __child) {
+ auto __old = get_scheduler(__env);
+ return transfer(let_value(transfer_just(std::move(__sched)),
+ __always{(_Child&&)__child}),
+ std::move(__old));
+ });
+ }
+};
+
+template <class _Scheduler, class _Closure>
+struct __continue_on_data
+{
+ _Scheduler __sched_;
+ _Closure __clsur_;
+};
+template <class _Scheduler, class _Closure>
+__continue_on_data(_Scheduler, _Closure)
+ -> __continue_on_data<_Scheduler, _Closure>;
+
+struct continue_on_t :
+ __with_default_get_env<continue_on_t>,
+ __no_scheduler_in_environment
+{
+ template <sender _Sender, scheduler _Scheduler,
+ __sender_adaptor_closure_for<_Sender> _Closure>
+ auto operator()(_Sender&& __sndr, _Scheduler&& __sched,
+ _Closure&& __clsur) const
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::transform_sender(
+ __domain,
+ __make_sexpr<continue_on_t>(
+ __continue_on_data{(_Scheduler&&)__sched, (_Closure&&)__clsur},
+ (_Sender&&)__sndr));
+ }
+
+ template <scheduler _Scheduler, __sender_adaptor_closure _Closure>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ auto operator()(_Scheduler&& __sched, _Closure&& __clsur) const
+ -> __binder_back<continue_on_t, __decay_t<_Scheduler>,
+ __decay_t<_Closure>>
+ {
+ return {{}, {}, {(_Scheduler&&)__sched, (_Closure&&)__clsur}};
+ }
+
+ using __no_scheduler_in_environment::transform_sender;
+
+ template <class _Sender, class _Env>
+ requires __callable<get_scheduler_t, const _Env&>
+ static auto transform_sender(_Sender&& __sndr, const _Env& __env)
+ {
+ auto __old = get_scheduler(__env);
+ return __sexpr_apply((_Sender&&)__sndr,
+ [&]<class _Data, class _Child>(
+ __ignore, _Data&& __data, _Child&& __child) {
+ auto&& [__sched, __clsur] = (_Data&&)__data;
+ using _Closure = decltype(__clsur);
+ return __write(transfer(((_Closure&&)__clsur)(transfer(
+ __write((_Child&&)__child,
+ __detail::__mkenv_sched(__old)),
+ __sched)),
+ __old),
+ __detail::__mkenv_sched(__sched));
+ });
+ }
+};
+} // namespace __on_v2
+
+namespace v2
+{
+using __on_v2::on_t;
+inline constexpr on_t on{};
+
+using __on_v2::continue_on_t;
+inline constexpr continue_on_t continue_on{};
+} // namespace v2
+
+/////////////////////////////////////////////////////////////////////////////
// [execution.senders.consumers.sync_wait]
// [execution.senders.consumers.sync_wait_with_variant]
namespace __sync_wait
{
-template <class _Sender>
-using __into_variant_result_t = decltype(into_variant(__declval<_Sender>()));
-
inline auto __make_env(run_loop& __loop) noexcept
{
return __env::__env_fn{
@@ -6999,21 +8386,26 @@
}};
}
-using __env = decltype(__sync_wait::__make_env(__declval<run_loop&>()));
+struct __env : __result_of<__make_env, run_loop&>
+{
+ __env();
+
+ __env(run_loop& __loop) noexcept :
+ __result_of<__make_env, run_loop&>{__sync_wait::__make_env(__loop)}
+ {}
+};
// What should sync_wait(just_stopped()) return?
-template <sender_in<__env> _Sender, class _Continuation>
-using __sync_wait_result_impl = __value_types_of_t<
- _Sender, __env, __transform<__q<__decay_t>, _Continuation>, __q<__msingle>>;
+template <class _Sender, class _Continuation>
+using __sync_wait_result_impl = //
+ __try_value_types_of_t<_Sender, __env,
+ __transform<__q<__decay_t>, _Continuation>,
+ __q<__msingle>>;
template <class _Sender>
using __sync_wait_result_t =
__mtry_eval<__sync_wait_result_impl, _Sender, __q<std::tuple>>;
-template <class _Sender>
-using __sync_wait_with_variant_result_t =
- __sync_wait_result_t<__into_variant_result_t<_Sender>>;
-
template <class... _Values>
struct __state
{
@@ -7075,35 +8467,150 @@
friend __env tag_invoke(get_env_t, const __t& __rcvr) noexcept
{
- return __sync_wait::__make_env(*__rcvr.__loop_);
+ return __env(*__rcvr.__loop_);
}
};
};
template <class _Sender>
-using __into_variant_result_t = decltype(into_variant(__declval<_Sender>()));
-
-struct sync_wait_t;
-
-using _Sender = __0;
-using __cust_sigs =
- __types<tag_invoke_t(sync_wait_t,
- get_completion_scheduler_t<set_value_t>(
- get_env_t(const _Sender&)),
- _Sender),
- tag_invoke_t(sync_wait_t, _Sender)>;
-
-template <class _Sender>
-inline constexpr bool __is_sync_wait_customized =
- __minvocable<__which<__cust_sigs>, _Sender>;
-
-template <class _Sender>
using __receiver_t = __t<__sync_wait_result_impl<_Sender, __q<__receiver>>>;
-struct __default_impl
+// These are for hiding the metaprogramming in diagnostics
+template <class _Sender>
+struct __sync_receiver_for
{
- template <class _Sender>
+ using __t = __receiver_t<_Sender>;
+};
+template <class _Sender>
+using __sync_receiver_for_t = __t<__sync_receiver_for<_Sender>>;
+
+template <class _Sender>
+struct __value_tuple_for
+{
+ using __t = __sync_wait_result_t<_Sender>;
+};
+template <class _Sender>
+using __value_tuple_for_t = __t<__value_tuple_for<_Sender>>;
+
+template <class _Sender>
+struct __variant_for
+{
+ using __t = __sync_wait_result_t<__result_of<into_variant, _Sender>>;
+};
+template <class _Sender>
+using __variant_for_t = __t<__variant_for<_Sender>>;
+
+inline constexpr __mstring __sync_wait_context_diag = //
+ "In stdexec::sync_wait()..."__csz;
+inline constexpr __mstring __too_many_successful_completions_diag =
+ "The argument to stdexec::sync_wait() is a sender that can complete successfully in more "
+ "than one way. Use stdexec::sync_wait_with_variant() instead."__csz;
+
+template <__mstring _Context, __mstring _Diagnostic>
+struct _INVALID_ARGUMENT_TO_SYNC_WAIT_;
+
+template <__mstring _Diagnostic>
+using __invalid_argument_to_sync_wait =
+ _INVALID_ARGUMENT_TO_SYNC_WAIT_<__sync_wait_context_diag, _Diagnostic>;
+
+template <__mstring _Diagnostic, class _Sender, class _Env = __env>
+using __sync_wait_error =
+ __mexception<__invalid_argument_to_sync_wait<_Diagnostic>,
+ _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
+
+template <class _Sender, class>
+using __too_many_successful_completions_error =
+ __sync_wait_error<__too_many_successful_completions_diag, _Sender>;
+
+template <class _Sender>
+concept __valid_sync_wait_argument =
+ __ok<__minvoke<__mtry_catch_q<__single_value_variant_sender_t,
+ __q<__too_many_successful_completions_error>>,
+ _Sender, __env>>;
+
+#if STDEXEC_NVHPC()
+// It requires some hoop-jumping to get the NVHPC compiler to report a
+// meaningful diagnostic for SFINAE failures.
+template <class _Sender>
+auto __diagnose_error()
+{
+ if constexpr (!sender_in<_Sender, __env>)
+ {
+ using _Completions = __completion_signatures_of_t<_Sender, __env>;
+ if constexpr (!__ok<_Completions>)
+ {
+ return _Completions();
+ }
+ else
+ {
+ constexpr __mstring __diag =
+ "The stdexec::sender_in<Sender, Environment> concept check has failed."__csz;
+ return __sync_wait_error<__diag, _Sender>();
+ }
+ }
+ else if constexpr (!__valid_sync_wait_argument<_Sender>)
+ {
+ return __sync_wait_error<__too_many_successful_completions_diag,
+ _Sender>();
+ }
+ else if constexpr (!sender_to<_Sender, __sync_receiver_for_t<_Sender>>)
+ {
+ constexpr __mstring __diag =
+ "Failed to connect the given sender to sync_wait's internal receiver. "
+ "The stdexec::connect(Sender, Receiver) expression is ill-formed."__csz;
+ return __sync_wait_error<__diag, _Sender>();
+ }
+ else
+ {
+ constexpr __mstring __diag = "Unknown concept check failure."__csz;
+ return __sync_wait_error<__diag, _Sender>();
+ }
+ STDEXEC_UNREACHABLE();
+}
+
+template <class _Sender>
+using __error_description_t =
+ decltype(__sync_wait::__diagnose_error<_Sender>());
+#endif
+
+////////////////////////////////////////////////////////////////////////////
+// [execution.senders.consumers.sync_wait]
+struct sync_wait_t
+{
+ template <sender_in<__env> _Sender>
+ requires __valid_sync_wait_argument<_Sender> &&
+ __has_implementation_for<sync_wait_t,
+ __early_domain_of_t<_Sender>, _Sender>
auto operator()(_Sender&& __sndr) const
+ -> std::optional<__value_tuple_for_t<_Sender>>
+ {
+ using _SD = __early_domain_of_t<_Sender>;
+ constexpr bool __has_custom_impl =
+ __callable<apply_sender_t, _SD, sync_wait_t, _Sender>;
+ using _Domain = __if_c<__has_custom_impl, _SD, default_domain>;
+ return stdexec::apply_sender(_Domain(), *this, (_Sender&&)__sndr);
+ }
+
+#if STDEXEC_NVHPC()
+ // This is needed to get sensible diagnostics from nvc++
+ template <class _Sender, class _Error = __error_description_t<_Sender>>
+ auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const
+ -> std::optional<std::tuple<int>> = delete;
+#endif
+
+ using _Sender = __0;
+ using __legacy_customizations_t = __types<
+ // For legacy reasons:
+ tag_invoke_t(
+ sync_wait_t,
+ get_completion_scheduler_t<set_value_t>(get_env_t(const _Sender&)),
+ _Sender),
+ tag_invoke_t(sync_wait_t, _Sender)>;
+
+ // The default implementation goes here:
+ template <class _Sender>
+ requires sender_to<_Sender, __sync_receiver_for_t<_Sender>>
+ auto apply_sender(_Sender&& __sndr) const
-> std::optional<__sync_wait_result_t<_Sender>>
{
using state_t = __sync_wait_result_impl<_Sender, __q<__state>>;
@@ -7129,108 +8636,55 @@
}
};
-template <class _Sender>
-using __dispatcher_for =
- __make_dispatcher<__cust_sigs, __mconst<__default_impl>, _Sender>;
-
-// These are for hiding the metaprogramming in diagnostics
-template <class _Sender>
-struct __sync_receiver_for
-{
- using __t = __receiver_t<_Sender>;
-};
-template <class _Sender>
-using __sync_receiver_for_t = __t<__sync_receiver_for<_Sender>>;
-
-template <class _Sender>
-struct __value_tuple_for
-{
- using __t = __sync_wait_result_t<_Sender>;
-};
-template <class _Sender>
-using __value_tuple_for_t = __t<__value_tuple_for<_Sender>>;
-
-////////////////////////////////////////////////////////////////////////////
-// [execution.senders.consumers.sync_wait]
-struct sync_wait_t
-{
- template <sender_in<__env> _Sender>
- requires __satisfies<__single_value_variant_sender<_Sender, __env>> &&
- (sender_to<_Sender, __sync_receiver_for_t<_Sender>> ||
- __is_sync_wait_customized<_Sender>)
- auto operator()(_Sender&& __sndr) const
- -> std::optional<__value_tuple_for_t<_Sender>>
- {
- // The selected implementation should return void
- return __dispatcher_for<_Sender>{}((_Sender&&)__sndr);
- }
-};
-
////////////////////////////////////////////////////////////////////////////
// [execution.senders.consumers.sync_wait_with_variant]
struct sync_wait_with_variant_t
{
- template <sender_in<__env> _Sender>
- requires __tag_invocable_with_completion_scheduler<
- sync_wait_with_variant_t, set_value_t, _Sender>
- tag_invoke_result_t<sync_wait_with_variant_t,
- __completion_scheduler_for<_Sender, set_value_t>,
- _Sender>
- operator()(_Sender&& __sndr) const
- noexcept(nothrow_tag_invocable<
- sync_wait_with_variant_t,
- __completion_scheduler_for<_Sender, set_value_t>, _Sender>)
- {
- static_assert(
- std::is_same_v<
- tag_invoke_result_t<
- sync_wait_with_variant_t,
- __completion_scheduler_for<_Sender, set_value_t>, _Sender>,
- std::optional<__sync_wait_with_variant_result_t<_Sender>>>,
- "The type of tag_invoke(sync_wait_with_variant, get_completion_scheduler, S) "
- "must be sync-wait-with-variant-type<S, sync-wait-env>");
+ struct __impl;
- auto __sched = get_completion_scheduler<set_value_t>(get_env(__sndr));
- return tag_invoke(sync_wait_with_variant_t{}, std::move(__sched),
- (_Sender&&)__sndr);
+ template <sender_in<__env> _Sender>
+ requires __callable<apply_sender_t, __early_domain_of_t<_Sender>,
+ sync_wait_with_variant_t, _Sender>
+ auto operator()(_Sender&& __sndr) const
+ -> std::optional<__variant_for_t<_Sender>>
+ {
+ auto __domain = __get_early_domain(__sndr);
+ return stdexec::apply_sender(__domain, *this, (_Sender&&)__sndr);
}
- template <sender_in<__env> _Sender>
- requires(!__tag_invocable_with_completion_scheduler<
- sync_wait_with_variant_t, set_value_t, _Sender>) &&
- tag_invocable<sync_wait_with_variant_t, _Sender>
- tag_invoke_result_t<sync_wait_with_variant_t, _Sender>
- operator()(_Sender&& __sndr) const
- noexcept(nothrow_tag_invocable<sync_wait_with_variant_t, _Sender>)
- {
- static_assert(
- std::is_same_v<
- tag_invoke_result_t<sync_wait_with_variant_t, _Sender>,
- std::optional<__sync_wait_with_variant_result_t<_Sender>>>,
- "The type of tag_invoke(sync_wait_with_variant, S) "
- "must be sync-wait-with-variant-type<S, sync-wait-env>");
+#if STDEXEC_NVHPC()
+ template <class _Sender, class _Error = __error_description_t<
+ __result_of<into_variant, _Sender>>>
+ auto operator()(_Sender&&, [[maybe_unused]] _Error __diagnostic = {}) const
+ -> std::optional<std::tuple<std::variant<std::tuple<>>>> = delete;
+#endif
- return tag_invoke(sync_wait_with_variant_t{}, (_Sender&&)__sndr);
- }
+ using _Sender = __0;
+ using __legacy_customizations_t = __types<
+ // For legacy reasons:
+ tag_invoke_t(
+ sync_wait_with_variant_t,
+ get_completion_scheduler_t<set_value_t>(get_env_t(const _Sender&)),
+ _Sender),
+ tag_invoke_t(sync_wait_with_variant_t, _Sender)>;
- template <sender_in<__env> _Sender>
- requires(!__tag_invocable_with_completion_scheduler<
- sync_wait_with_variant_t, set_value_t, _Sender>) &&
- (!tag_invocable<sync_wait_with_variant_t, _Sender>) &&
- invocable<sync_wait_t, __into_variant_result_t<_Sender>>
- std::optional<__sync_wait_with_variant_result_t<_Sender>>
- operator()(_Sender&& __sndr) const
+ template <class _Sender>
+ requires __callable<sync_wait_t, __result_of<into_variant, _Sender>>
+ auto apply_sender(_Sender&& __sndr) const
+ -> std::optional<__variant_for_t<_Sender>>
{
- return sync_wait_t{}(into_variant((_Sender&&)__sndr));
+ return sync_wait_t()(into_variant((_Sender&&)__sndr));
}
};
} // namespace __sync_wait
using __sync_wait::sync_wait_t;
inline constexpr sync_wait_t sync_wait{};
+
using __sync_wait::sync_wait_with_variant_t;
inline constexpr sync_wait_with_variant_t sync_wait_with_variant{};
+//////////////////////////////////////////////////////////////////////////////////////////////////
struct __ignore_sender
{
using is_sender = void;
@@ -7245,14 +8699,135 @@
{};
template <class _Sender>
-struct _WITH_SENDER_
-{};
-
-template <class _Sender>
using __bad_pipe_sink_t =
__mexception<_CANNOT_PIPE_INTO_A_SENDER_<>, _WITH_SENDER_<_Sender>>;
} // namespace stdexec
+#if STDEXEC_MSVC()
+namespace stdexec
+{
+// MSVCBUG
+// https://developercommunity.visualstudio.com/t/Incorrect-codegen-in-await_suspend-aroun/10454102
+
+// MSVC incorrectly allocates the return buffer for await_suspend calls within
+// the suspended coroutine frame. When the suspended coroutine is destroyed
+// within await_suspend, the continuation coroutine handle is not only used
+// after free, but also overwritten by the debug malloc implementation when NRVO
+// is in play.
+
+// This workaround delays the destruction of the suspended coroutine by wrapping
+// the continuation in another coroutine which destroys the former and transfers
+// execution to the original continuation.
+
+// The wrapping coroutine is thread-local and is reused within the thread for
+// each destroy-and-continue sequence. The wrapping coroutine itself is
+// destroyed at thread exit.
+
+namespace __destroy_and_continue_msvc
+{
+struct __task
+{
+ struct promise_type
+ {
+ __task get_return_object() noexcept
+ {
+ return {
+ __coro::coroutine_handle<promise_type>::from_promise(*this)};
+ }
+
+ static std::suspend_never initial_suspend() noexcept
+ {
+ return {};
+ }
+
+ static std::suspend_never final_suspend() noexcept
+ {
+ STDEXEC_ASSERT(!"Should never get here");
+ return {};
+ }
+
+ static void return_void() noexcept
+ {
+ STDEXEC_ASSERT(!"Should never get here");
+ }
+
+ static void unhandled_exception() noexcept
+ {
+ STDEXEC_ASSERT(!"Should never get here");
+ }
+ };
+
+ __coro::coroutine_handle<> __coro_;
+};
+
+struct __continue_t
+{
+ static constexpr bool await_ready() noexcept
+ {
+ return false;
+ }
+
+ __coro::coroutine_handle<>
+ await_suspend(__coro::coroutine_handle<>) noexcept
+ {
+ return __continue_;
+ }
+
+ static void await_resume() noexcept {}
+
+ __coro::coroutine_handle<> __continue_;
+};
+
+struct __context
+{
+ __coro::coroutine_handle<> __destroy_;
+ __coro::coroutine_handle<> __continue_;
+};
+
+inline __task __co_impl(__context& __c)
+{
+ while (true)
+ {
+ co_await __continue_t{__c.__continue_};
+ __c.__destroy_.destroy();
+ }
+}
+
+struct __context_and_coro
+{
+ __context_and_coro()
+ {
+ __context_.__continue_ = __coro::noop_coroutine();
+ __coro_ = __co_impl(__context_).__coro_;
+ }
+
+ ~__context_and_coro()
+ {
+ __coro_.destroy();
+ }
+
+ __context __context_;
+ __coro::coroutine_handle<> __coro_;
+};
+
+inline __coro::coroutine_handle<> __impl(__coro::coroutine_handle<> __destroy,
+ __coro::coroutine_handle<> __continue)
+{
+ static thread_local __context_and_coro __c;
+ __c.__context_.__destroy_ = __destroy;
+ __c.__context_.__continue_ = __continue;
+ return __c.__coro_;
+}
+} // namespace __destroy_and_continue_msvc
+} // namespace stdexec
+
+#define STDEXEC_DESTROY_AND_CONTINUE(__destroy, __continue) \
+ (::stdexec::__destroy_and_continue_msvc::__impl(__destroy, __continue))
+#else
+#define STDEXEC_DESTROY_AND_CONTINUE(__destroy, __continue) \
+ (__destroy.destroy(), __continue)
+#endif
+
// For issuing a meaningful diagnostic for the erroneous `snd1 | snd2`.
template <stdexec::sender _Sender>
requires stdexec::__ok<stdexec::__bad_pipe_sink_t<_Sender>>
@@ -7261,8 +8836,4 @@
#include "__detail/__p2300.hpp"
-#ifdef __EDG__
-#pragma diagnostic pop
-#endif
-
STDEXEC_PRAGMA_POP()
diff --git a/include/sdbusplus/async/stdexec/functional.hpp b/include/sdbusplus/async/stdexec/functional.hpp
index 0f76474..b0bcc0f 100644
--- a/include/sdbusplus/async/stdexec/functional.hpp
+++ b/include/sdbusplus/async/stdexec/functional.hpp
@@ -20,9 +20,7 @@
#include "concepts.hpp"
#include <functional>
-
-// A std::declval that doesn't instantiate templates:
-#define _DECLVAL(...) ((static_cast<__VA_ARGS__ (*)() noexcept>(0))())
+#include <tuple>
namespace stdexec::__std_concepts
{
@@ -44,41 +42,334 @@
namespace stdexec
{
-template <class _Fun, class... _As>
-concept __nothrow_invocable = //
- invocable<_Fun, _As...> && //
- requires(_Fun&& __f, _As&&... __as) {
- {
- std::invoke((_Fun&&)__f, (_As&&)__as...)
- } noexcept;
- };
-
-struct __first
-{
- template <class _First, class _Second>
- constexpr _First&& operator()(_First&& __first, _Second&&) const noexcept
- {
- return (_First&&)__first;
- }
-};
-
template <auto _Fun>
-struct __fun_c_t
+struct __function_constant
{
using _FunT = decltype(_Fun);
template <class... _Args>
requires __callable<_FunT, _Args...>
- auto operator()(_Args&&... __args) const
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ auto
+ operator()(_Args&&... __args) const
noexcept(noexcept(_Fun((_Args&&)__args...)))
- -> __call_result_t<_FunT, _Args...>
+ -> decltype(_Fun((_Args&&)__args...))
{
return _Fun((_Args&&)__args...);
}
};
+template <class _Ty, class _Cl, _Ty _Cl::*_MemPtr>
+struct __function_constant<_MemPtr>
+{
+ using _FunT = _Ty _Cl::*;
+
+ template <class _Arg>
+ requires requires(_Arg&& __arg) { ((_Arg&&)__arg).*_MemPtr; }
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto
+ operator()(_Arg&& __arg) const noexcept
+ -> decltype((((_Arg&&)__arg).*_MemPtr))
+ {
+ return ((_Arg&&)__arg).*_MemPtr;
+ }
+};
+
template <auto _Fun>
-inline constexpr __fun_c_t<_Fun> __fun_c{};
+inline constexpr __function_constant<_Fun> __function_constant_v{};
+
+template <class _Fun0, class _Fun1>
+struct __composed
+{
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fun0 __t0_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fun1 __t1_;
+
+ template <class... _Ts>
+ requires __callable<_Fun1, _Ts...> &&
+ __callable<_Fun0, __call_result_t<_Fun1, _Ts...>>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ __call_result_t<_Fun0, __call_result_t<_Fun1, _Ts...>>
+ operator()(_Ts&&... __ts) &&
+ {
+ return ((_Fun0&&)__t0_)(((_Fun1&&)__t1_)((_Ts&&)__ts...));
+ }
+
+ template <class... _Ts>
+ requires __callable<const _Fun1&, _Ts...> &&
+ __callable<const _Fun0&, __call_result_t<const _Fun1&, _Ts...>>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ __call_result_t<_Fun0, __call_result_t<_Fun1, _Ts...>>
+ operator()(_Ts&&... __ts) const&
+ {
+ return __t0_(__t1_((_Ts&&)__ts...));
+ }
+};
+
+inline constexpr struct __compose_t
+{
+ template <class _Fun0, class _Fun1>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ __composed<_Fun0, _Fun1> operator()(_Fun0 __fun0, _Fun1 __fun1) const
+ {
+ return {(_Fun0&&)__fun0, (_Fun1&&)__fun1};
+ }
+} __compose{};
+
+namespace __invoke_
+{
+template <class>
+inline constexpr bool __is_refwrap = false;
+template <class _Up>
+inline constexpr bool __is_refwrap<std::reference_wrapper<_Up>> = true;
+
+struct __funobj
+{
+ template <class _Fun, class... _Args>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto operator()(_Fun&& __fun, _Args&&... __args) const
+ noexcept(noexcept(((_Fun&&)__fun)((_Args&&)__args...)))
+ -> decltype(((_Fun&&)__fun)((_Args&&)__args...))
+ {
+ return ((_Fun&&)__fun)((_Args&&)__args...);
+ }
+};
+
+struct __memfn
+{
+ template <class _Memptr, class _Ty, class... _Args>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto operator()(_Memptr __mem_ptr, _Ty&& __ty,
+ _Args&&... __args) const
+ noexcept(noexcept((((_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...)))
+ -> decltype((((_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...))
+ {
+ return (((_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...);
+ }
+};
+
+struct __memfn_refwrap
+{
+ template <class _Memptr, class _Ty, class... _Args>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto operator()(_Memptr __mem_ptr, _Ty __ty,
+ _Args&&... __args) const
+ noexcept(noexcept((__ty.get().*__mem_ptr)((_Args&&)__args...)))
+ -> decltype((__ty.get().*__mem_ptr)((_Args&&)__args...))
+ {
+ return (__ty.get().*__mem_ptr)((_Args&&)__args...);
+ }
+};
+
+struct __memfn_smartptr
+{
+ template <class _Memptr, class _Ty, class... _Args>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto operator()(_Memptr __mem_ptr, _Ty&& __ty,
+ _Args&&... __args) const
+ noexcept(noexcept(((*(_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...)))
+ -> decltype(((*(_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...))
+ {
+ return ((*(_Ty&&)__ty).*__mem_ptr)((_Args&&)__args...);
+ }
+};
+
+struct __memobj
+{
+ template <class _Mbr, class _Class, class _Ty>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto operator()(_Mbr _Class::*__mem_ptr,
+ _Ty&& __ty) const noexcept
+ -> decltype((((_Ty&&)__ty).*__mem_ptr))
+ {
+ return (((_Ty&&)__ty).*__mem_ptr);
+ }
+};
+
+struct __memobj_refwrap
+{
+ template <class _Mbr, class _Class, class _Ty>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto operator()(_Mbr _Class::*__mem_ptr, _Ty __ty) const noexcept
+ -> decltype((__ty.get().*__mem_ptr))
+ {
+ return (__ty.get().*__mem_ptr);
+ }
+};
+
+struct __memobj_smartptr
+{
+ template <class _Mbr, class _Class, class _Ty>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto operator()(_Mbr _Class::*__mem_ptr,
+ _Ty&& __ty) const noexcept
+ -> decltype(((*(_Ty&&)__ty).*__mem_ptr))
+ {
+ return ((*(_Ty&&)__ty).*__mem_ptr);
+ }
+};
+
+__funobj __invoke_selector(__ignore, __ignore) noexcept;
+
+template <class _Mbr, class _Class, class _Ty>
+auto __invoke_selector(_Mbr _Class::*, const _Ty&) noexcept
+{
+ if constexpr (STDEXEC_IS_CONST(_Mbr) || STDEXEC_IS_CONST(const _Mbr))
+ {
+ // member function ptr case
+ if constexpr (STDEXEC_IS_BASE_OF(_Class, _Ty))
+ {
+ return __memobj{};
+ }
+ else if constexpr (__is_refwrap<_Ty>)
+ {
+ return __memobj_refwrap{};
+ }
+ else
+ {
+ return __memobj_smartptr{};
+ }
+ }
+ else
+ {
+ // member object ptr case
+ if constexpr (STDEXEC_IS_BASE_OF(_Class, _Ty))
+ {
+ return __memfn{};
+ }
+ else if constexpr (__is_refwrap<_Ty>)
+ {
+ return __memfn_refwrap{};
+ }
+ else
+ {
+ return __memfn_smartptr{};
+ }
+ }
+}
+
+struct __invoke_t
+{
+ template <class _Fun>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto operator()(_Fun&& __fun) const
+ noexcept(noexcept(((_Fun&&)__fun)())) -> decltype(((_Fun&&)__fun)())
+ {
+ return ((_Fun&&)__fun)();
+ }
+
+ template <class _Fun, class _Ty, class... _Args>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto operator()(_Fun&& __fun, _Ty&& __ty, _Args&&... __args) const
+ noexcept(noexcept(__invoke_selector(__fun, __ty)((_Fun&&)__fun,
+ (_Ty&&)__ty,
+ (_Args&&)__args...)))
+ -> decltype(__invoke_selector(__fun, __ty)((_Fun&&)__fun,
+ (_Ty&&)__ty,
+ (_Args&&)__args...))
+ {
+ return decltype(__invoke_selector(__fun, __ty))()(
+ (_Fun&&)__fun, (_Ty&&)__ty, (_Args&&)__args...);
+ }
+};
+} // namespace __invoke_
+
+inline constexpr __invoke_::__invoke_t __invoke{};
+
+template <class _Fun, class... _As>
+concept __invocable = //
+ requires(_Fun&& __f, _As&&... __as) {
+ __invoke((_Fun&&)__f, (_As&&)__as...);
+ };
+
+template <class _Fun, class... _As>
+concept __nothrow_invocable = //
+ __invocable<_Fun, _As...> && //
+ requires(_Fun&& __f, _As&&... __as) {
+ {
+ __invoke((_Fun&&)__f, (_As&&)__as...)
+ } noexcept;
+ };
+
+template <class _Fun, class... _As>
+using __invoke_result_t = //
+ decltype(__invoke(__declval<_Fun>(), __declval<_As>()...));
+
+namespace __apply_
+{
+using std::get;
+
+template <std::size_t... _Is, class _Fn, class _Tup>
+STDEXEC_ATTRIBUTE((always_inline)) //
+constexpr auto __impl(__indices<_Is...>, _Fn&& __fn, _Tup&& __tup) noexcept(
+ noexcept(__invoke((_Fn&&)__fn, get<_Is>((_Tup&&)__tup)...)))
+ -> decltype(__invoke((_Fn&&)__fn, get<_Is>((_Tup&&)__tup)...))
+{
+ return __invoke((_Fn&&)__fn, get<_Is>((_Tup&&)__tup)...);
+}
+
+template <class _Tup>
+using __tuple_indices =
+ __make_indices<std::tuple_size<std::remove_cvref_t<_Tup>>::value>;
+
+template <class _Fn, class _Tup>
+using __result_t = decltype(__apply_::__impl(
+ __tuple_indices<_Tup>(), __declval<_Fn>(), __declval<_Tup>()));
+} // namespace __apply_
+
+template <class _Fn, class _Tup>
+concept __applicable = __mvalid<__apply_::__result_t, _Fn, _Tup>;
+
+template <class _Fn, class _Tup>
+concept __nothrow_applicable = __applicable<_Fn, _Tup> //
+ && noexcept(__apply_::__impl(__apply_::__tuple_indices<_Tup>(),
+ __declval<_Fn>(), __declval<_Tup>()));
+
+template <class _Fn, class _Tup>
+ requires __applicable<_Fn, _Tup>
+using __apply_result_t = __apply_::__result_t<_Fn, _Tup>;
+
+struct __apply_t
+{
+ template <class _Fn, class _Tup>
+ requires __applicable<_Fn, _Tup>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ constexpr auto
+ operator()(_Fn&& __fn, _Tup&& __tup) const
+ noexcept(__nothrow_applicable<_Fn, _Tup>) -> __apply_result_t<_Fn, _Tup>
+ {
+ return __apply_::__impl(__apply_::__tuple_indices<_Tup>(), (_Fn&&)__fn,
+ (_Tup&&)__tup);
+ }
+};
+
+inline constexpr __apply_t __apply{};
+
+template <class _Tag, class _Ty>
+struct __field
+{
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ _Ty operator()(_Tag) const noexcept(__nothrow_decay_copyable<const _Ty&>)
+ {
+ return __t_;
+ }
+
+ _Ty __t_;
+};
+
+template <class _Tag>
+struct __mkfield_
+{
+ template <class _Ty>
+ STDEXEC_ATTRIBUTE((always_inline)) //
+ __field<_Tag, __decay_t<_Ty>> operator()(_Ty&& __ty) const
+ noexcept(__nothrow_decay_copyable<_Ty>)
+ {
+ return {(_Ty&&)__ty};
+ }
+};
+
+template <class _Tag>
+inline constexpr __mkfield_<_Tag> __mkfield{};
// [func.tag_invoke], tag_invoke
namespace __tag_invoke
@@ -131,17 +422,25 @@
{
template <class _Tag, class... _Args>
requires tag_invocable<_Tag, _Args...>
- constexpr auto operator()(_Tag __tag, _Args&&... __args) const
+ STDEXEC_ATTRIBUTE((always_inline)) constexpr auto
+ operator()(_Tag __tag, _Args&&... __args) const
noexcept(nothrow_tag_invocable<_Tag, _Args...>)
-> tag_invoke_result_t<_Tag, _Args...>
{
return tag_invoke((_Tag&&)__tag, (_Args&&)__args...);
}
};
+
} // namespace __tag_invoke
using __tag_invoke::tag_invoke_t;
+
+namespace __ti
+{
inline constexpr tag_invoke_t tag_invoke{};
+}
+
+using namespace __ti;
template <auto& _Tag>
using tag_t = __decay_t<decltype(_Tag)>;
diff --git a/include/sdbusplus/async/stdexec/import b/include/sdbusplus/async/stdexec/import
index 4ba6a80..df69427 100755
--- a/include/sdbusplus/async/stdexec/import
+++ b/include/sdbusplus/async/stdexec/import
@@ -4,11 +4,12 @@
git -C "${execution_dir}" rev-parse HEAD > commit.info
cp -r "${execution_dir}"/include/stdexec/* .
-cp "${execution_dir}"/include/exec/{any_sender_of,async_scope,at_coroutine_exit,env,inline_scheduler,scope,task}.hpp .
+cp "${execution_dir}"/include/exec/{any_sender_of,async_scope,at_coroutine_exit,env,inline_scheduler,scope,task,sequence_senders}.hpp .
(find . -name "*.hpp" -print0 || true) | while IFS= read -r -d '' f
do
sed -i "s#include <stdexec/#include <sdbusplus/async/stdexec/#" -- "${f}"
sed -i "s#include <exec/#include <sdbusplus/async/stdexec/#" -- "${f}"
+ sed -i 's#include "\./\([^"]*\)"#include <sdbusplus/async/stdexec/\1>#' -- "${f}"
clang-format -i "${f}"
done
diff --git a/include/sdbusplus/async/stdexec/inline_scheduler.hpp b/include/sdbusplus/async/stdexec/inline_scheduler.hpp
index 8adda7f..f100590 100644
--- a/include/sdbusplus/async/stdexec/inline_scheduler.hpp
+++ b/include/sdbusplus/async/stdexec/inline_scheduler.hpp
@@ -24,66 +24,5 @@
{
// A simple scheduler that executes its continuation inline, on the
// thread of the caller of start().
-struct inline_scheduler
-{
- template <class R_>
- struct __op
- {
- using R = stdexec::__t<R_>;
- STDEXEC_NO_UNIQUE_ADDRESS R rec_;
-
- friend void tag_invoke(stdexec::start_t, __op& op) noexcept
- {
- stdexec::set_value((R&&)op.rec_);
- }
- };
-
- struct __sender
- {
- using is_sender = void;
- using completion_signatures =
- stdexec::completion_signatures<stdexec::set_value_t()>;
-
- template <class R>
- friend auto tag_invoke(stdexec::connect_t, __sender, R&& rec) //
- noexcept(
- stdexec::__nothrow_constructible_from<stdexec::__decay_t<R>, R>)
- -> __op<stdexec::__x<stdexec::__decay_t<R>>>
- {
- return {(R&&)rec};
- }
-
- struct __env
- {
- friend inline_scheduler tag_invoke(
- stdexec::get_completion_scheduler_t<stdexec::set_value_t>,
- const __env&) //
- noexcept
- {
- return {};
- }
- };
-
- friend __env tag_invoke(stdexec::get_env_t, const __sender&) noexcept
- {
- return {};
- }
- };
-
- STDEXEC_DETAIL_CUDACC_HOST_DEVICE //
- friend __sender
- tag_invoke(stdexec::schedule_t, const inline_scheduler&) noexcept
- {
- return {};
- }
-
- friend stdexec::forward_progress_guarantee
- tag_invoke(stdexec::get_forward_progress_guarantee_t,
- const inline_scheduler&) noexcept
- {
- return stdexec::forward_progress_guarantee::weakly_parallel;
- }
-
- bool operator==(const inline_scheduler&) const noexcept = default;
-};
+using inline_scheduler = stdexec::__inln::__scheduler;
} // namespace exec
diff --git a/include/sdbusplus/async/stdexec/sequence_senders.hpp b/include/sdbusplus/async/stdexec/sequence_senders.hpp
new file mode 100644
index 0000000..88412e6
--- /dev/null
+++ b/include/sdbusplus/async/stdexec/sequence_senders.hpp
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2023 Maikel Nadolski
+ * Copyright (c) 2023 NVIDIA Corporation
+ *
+ * Licensed under the Apache License Version 2.0 with LLVM Exceptions
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * https://llvm.org/LICENSE.txt
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "../stdexec/execution.hpp"
+
+namespace exec
+{
+struct sequence_tag
+{};
+
+namespace __sequence_sndr
+{
+using namespace stdexec;
+
+template <class _Haystack>
+struct __mall_contained_in_impl
+{
+ template <class... _Needles>
+ using __f = __mand<__mapply<__contains<_Needles>, _Haystack>...>;
+};
+
+template <class _Needles, class _Haystack>
+using __mall_contained_in =
+ __mapply<__mall_contained_in_impl<_Haystack>, _Needles>;
+
+template <class _Needles, class _Haystack>
+concept __all_contained_in = __mall_contained_in<_Needles, _Haystack>::value;
+
+// This concept checks if a given sender satisfies the requirements to be
+// returned from `set_next`.
+template <class _Sender, class _Env = empty_env>
+concept next_sender = //
+ sender_in<_Sender, _Env> //
+ &&
+ __all_contained_in<completion_signatures_of_t<_Sender, _Env>,
+ completion_signatures<set_value_t(), set_stopped_t()>>;
+
+// This is a sequence-receiver CPO that is used to apply algorithms on an input
+// sender and it returns a next-sender. `set_next` is usually called in a
+// context where a sender will be connected to a receiver. Since calling
+// `set_next` usually involves constructing senders it is allowed to throw an
+// excpetion, which needs to be handled by a calling sequence-operation. The
+// returned object is a sender that can complete with `set_value_t()` or
+// `set_stopped_t()`.
+struct set_next_t
+{
+ template <receiver _Receiver, sender _Item>
+ requires tag_invocable<set_next_t, _Receiver&, _Item>
+ auto operator()(_Receiver& __rcvr, _Item&& __item) const
+ noexcept(nothrow_tag_invocable<set_next_t, _Receiver&, _Item>)
+ -> tag_invoke_result_t<set_next_t, _Receiver&, _Item>
+ {
+ static_assert(
+ next_sender<tag_invoke_result_t<set_next_t, _Receiver&, _Item>>,
+ "The sender returned from set_next is required to complete with set_value_t() or "
+ "set_stopped_t()");
+ return tag_invoke(*this, __rcvr, (_Item&&)__item);
+ }
+};
+} // namespace __sequence_sndr
+
+using __sequence_sndr::set_next_t;
+inline constexpr set_next_t set_next;
+
+template <class _Receiver, class _Sender>
+using next_sender_of_t = decltype(exec::set_next(
+ stdexec::__declval<stdexec::__decay_t<_Receiver>&>(),
+ stdexec::__declval<_Sender>()));
+
+namespace __sequence_sndr
+{
+
+template <class _ReceiverId>
+struct __stopped_means_break
+{
+ struct __t
+ {
+ using is_receiver = void;
+ using __id = __stopped_means_break;
+ using _Receiver = stdexec::__t<_ReceiverId>;
+ using _Token = stop_token_of_t<env_of_t<_Receiver>>;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Receiver __rcvr_;
+
+ template <same_as<get_env_t> _GetEnv, same_as<__t> _Self>
+ friend env_of_t<_Receiver> tag_invoke(_GetEnv,
+ const _Self& __self) noexcept
+ {
+ return stdexec::get_env(__self.__rcvr_);
+ }
+
+ template <same_as<set_value_t> _SetValue, same_as<__t> _Self>
+ requires __callable<set_value_t, _Receiver&&>
+ friend void tag_invoke(_SetValue, _Self&& __self) noexcept
+ {
+ return stdexec::set_value(static_cast<_Receiver&&>(__self.__rcvr_));
+ }
+
+ template <same_as<set_stopped_t> _SetStopped, same_as<__t> _Self>
+ requires __callable<set_value_t, _Receiver&&> &&
+ (unstoppable_token<_Token> ||
+ __callable<set_stopped_t, _Receiver &&>)
+ friend void tag_invoke(_SetStopped, _Self&& __self) noexcept
+ {
+ if constexpr (unstoppable_token<_Token>)
+ {
+ stdexec::set_value(static_cast<_Receiver&&>(__self.__rcvr_));
+ }
+ else
+ {
+ auto __token =
+ stdexec::get_stop_token(stdexec::get_env(__self.__rcvr_));
+ if (__token.stop_requested())
+ {
+ stdexec::set_stopped(
+ static_cast<_Receiver&&>(__self.__rcvr_));
+ }
+ else
+ {
+ stdexec::set_value(
+ static_cast<_Receiver&&>(__self.__rcvr_));
+ }
+ }
+ }
+ };
+};
+
+template <class _Rcvr>
+using __stopped_means_break_t =
+ __t<__stopped_means_break<__id<__decay_t<_Rcvr>>>>;
+} // namespace __sequence_sndr
+
+template <class _Sender>
+concept __enable_sequence_sender = //
+ requires { typename _Sender::is_sender; } && //
+ stdexec::same_as<typename _Sender::is_sender, sequence_tag>;
+
+template <class _Sender>
+inline constexpr bool enable_sequence_sender =
+ __enable_sequence_sender<_Sender>;
+
+template <class... _Senders>
+struct item_types
+{};
+
+template <class _Tp>
+concept __has_item_typedef = requires { typename _Tp::item_types; };
+
+/////////////////////////////////////////////////////////////////////////////
+// [execution.sndtraits]
+namespace __sequence_sndr
+{
+struct get_item_types_t;
+template <class _Sender, class _Env>
+using __tfx_sender =
+ transform_sender_result_t<__late_domain_of_t<_Sender, _Env>, _Sender, _Env>;
+
+template <class _Sender, class _Env>
+concept __with_tag_invoke = //
+ tag_invocable<get_item_types_t, __tfx_sender<_Sender, _Env>, _Env>;
+template <class _Sender, class _Env>
+using __member_alias_t = //
+ typename __decay_t<__tfx_sender<_Sender, _Env>>::item_types;
+
+template <class _Sender, class _Env>
+concept __with_member_alias = __mvalid<__member_alias_t, _Sender, _Env>;
+
+struct get_item_types_t
+{
+ template <class _Sender, class _Env>
+ static auto __impl()
+ {
+ static_assert(sizeof(_Sender),
+ "Incomplete type used with get_item_types");
+ static_assert(sizeof(_Env), "Incomplete type used with get_item_types");
+ using _TfxSender = __tfx_sender<_Sender, _Env>;
+ if constexpr (__with_tag_invoke<_Sender, _Env>)
+ {
+ using _Result =
+ tag_invoke_result_t<get_item_types_t, _TfxSender, _Env>;
+ return (_Result(*)()) nullptr;
+ }
+ else if constexpr (__with_member_alias<_TfxSender, _Env>)
+ {
+ using _Result = __member_alias_t<_TfxSender, _Env>;
+ return (_Result(*)()) nullptr;
+ }
+ else if constexpr (sender_in<_TfxSender, _Env> &&
+ !enable_sequence_sender<
+ stdexec::__decay_t<_TfxSender>>)
+ {
+ using _Result = item_types<stdexec::__decay_t<_TfxSender>>;
+ return (_Result(*)()) nullptr;
+ }
+ else if constexpr (__is_debug_env<_Env>)
+ {
+ using __tag_invoke::tag_invoke;
+ // This ought to cause a hard error that indicates where the problem
+ // is.
+ using _Completions [[maybe_unused]] =
+ tag_invoke_result_t<get_item_types_t,
+ __tfx_sender<_Sender, _Env>, _Env>;
+ return (__debug::__completion_signatures(*)()) nullptr;
+ }
+ else
+ {
+ using _Result =
+ __mexception<_UNRECOGNIZED_SENDER_TYPE_<>,
+ _WITH_SENDER_<_Sender>, _WITH_ENVIRONMENT_<_Env>>;
+ return (_Result(*)()) nullptr;
+ }
+ }
+
+ template <class _Sender, class _Env = __default_env>
+ constexpr auto operator()(_Sender&&, const _Env&) const noexcept
+ -> decltype(__impl<_Sender, _Env>()())
+ {
+ return {};
+ }
+};
+} // namespace __sequence_sndr
+
+using __sequence_sndr::get_item_types_t;
+inline constexpr get_item_types_t get_item_types{};
+
+template <class _Sender, class _Env>
+using item_types_of_t = decltype(get_item_types(stdexec::__declval<_Sender>(),
+ stdexec::__declval<_Env>()));
+
+template <class _Sender, class _Env>
+concept sequence_sender = //
+ stdexec::sender_in<_Sender, _Env> && //
+ enable_sequence_sender<stdexec::__decay_t<_Sender>>;
+
+template <class _Sender, class _Env>
+concept has_sequence_item_types =
+ requires(_Sender&& __sndr, _Env&& __env) {
+ get_item_types((_Sender&&)__sndr, (_Env&&)__env);
+ };
+
+template <class _Sender, class _Env>
+concept sequence_sender_in = //
+ stdexec::sender_in<_Sender, _Env> && //
+ has_sequence_item_types<_Sender, _Env> && //
+ sequence_sender<_Sender, _Env>;
+
+template <class _Receiver>
+struct _WITH_RECEIVER_
+{};
+
+template <class _Item>
+struct _MISSING_SET_NEXT_OVERLOAD_FOR_ITEM_
+{};
+
+template <class _Receiver, class _Item>
+auto __try_item(_Item*)
+ -> stdexec::__mexception<_MISSING_SET_NEXT_OVERLOAD_FOR_ITEM_<_Item>,
+ _WITH_RECEIVER_<_Receiver>>;
+
+template <class _Receiver, class _Item>
+ requires stdexec::__callable<set_next_t, _Receiver&, _Item>
+stdexec::__msuccess __try_item(_Item*);
+
+template <class _Receiver, class... _Items>
+auto __try_items(exec::item_types<_Items...>*)
+ -> decltype((stdexec::__msuccess(), ...,
+ exec::__try_item<_Receiver>((_Items*)nullptr)));
+
+template <class _Receiver, class _Items>
+concept __sequence_receiver_of =
+ requires(_Items* __items) {
+ {
+ exec::__try_items<stdexec::__decay_t<_Receiver>>(__items)
+ } -> stdexec::__ok;
+ };
+
+template <class _Receiver, class _SequenceItems>
+concept sequence_receiver_of = //
+ stdexec::receiver<_Receiver> && //
+ __sequence_receiver_of<_Receiver, _SequenceItems>;
+
+template <class _Items, class _Env>
+using __concat_item_signatures_t = stdexec::__mapply<
+ stdexec::__q<stdexec::__concat_completion_signatures_t>,
+ stdexec::__mapply<stdexec::__transform<stdexec::__mbind_back_q<
+ stdexec::completion_signatures_of_t, _Env>>,
+ _Items>>;
+
+template <class _Completions>
+using __gather_error_signals =
+ stdexec::__only_gather_signal<stdexec::set_error_t, _Completions>;
+
+template <class _Completions>
+using __gather_stopped_signals =
+ stdexec::__only_gather_signal<stdexec::set_stopped_t, _Completions>;
+
+template <class _Completions>
+using __to_sequence_completions_t = stdexec::__concat_completion_signatures_t<
+ stdexec::completion_signatures<stdexec::set_value_t()>,
+ __gather_error_signals<_Completions>,
+ __gather_stopped_signals<_Completions>>;
+
+template <class _Sender, class _Env>
+using __to_sequence_completion_signatures = stdexec::make_completion_signatures<
+ _Sender, _Env, stdexec::completion_signatures<stdexec::set_value_t()>,
+ stdexec::__mconst<stdexec::completion_signatures<>>::__f>;
+
+template <class _Sequence, class _Env>
+using __sequence_completion_signatures_of_t =
+ stdexec::__concat_completion_signatures_t<
+ stdexec::__try_make_completion_signatures<
+ _Sequence, _Env,
+ stdexec::completion_signatures<stdexec::set_value_t()>,
+ stdexec::__mconst<stdexec::completion_signatures<>>>,
+ stdexec::__mapply<
+ stdexec::__q<stdexec::__concat_completion_signatures_t>,
+ stdexec::__mapply<stdexec::__transform<stdexec::__mbind_back_q<
+ __to_sequence_completion_signatures, _Env>>,
+ item_types_of_t<_Sequence, _Env>>>>;
+
+template <class _Receiver, class _Sender>
+concept sequence_receiver_from = //
+ stdexec::receiver<_Receiver> && //
+ stdexec::sender_in<_Sender, stdexec::env_of_t<_Receiver>> && //
+ sequence_receiver_of<
+ _Receiver, item_types_of_t<_Sender, stdexec::env_of_t<_Receiver>>> && //
+ ((sequence_sender_in<_Sender, stdexec::env_of_t<_Receiver>> &&
+ stdexec::receiver_of<_Receiver,
+ stdexec::completion_signatures_of_t<
+ _Sender, stdexec::env_of_t<_Receiver>>>) || //
+ (!sequence_sender_in<_Sender, stdexec::env_of_t<_Receiver>> &&
+ stdexec::__receiver_from<
+ __sequence_sndr::__stopped_means_break_t<_Receiver>,
+ next_sender_of_t<_Receiver, _Sender>>));
+
+namespace __sequence_sndr
+{
+struct subscribe_t;
+
+template <class _Env>
+using __single_sender_completion_sigs =
+ __if_c<unstoppable_token<stop_token_of_t<_Env>>,
+ completion_signatures<set_value_t()>,
+ completion_signatures<set_value_t(), set_stopped_t()>>;
+
+template <class _Sender, class _Receiver>
+concept __next_connectable_with_tag_invoke =
+ receiver<_Receiver> && //
+ sender_in<_Sender, env_of_t<_Receiver>> && //
+ !sequence_sender_in<_Sender, env_of_t<_Receiver>> && //
+ sequence_receiver_of<_Receiver,
+ item_types<stdexec::__decay_t<_Sender>>> && //
+ __receiver_from<__stopped_means_break_t<_Receiver>,
+ next_sender_of_t<_Receiver, _Sender>> && //
+ __connect::__connectable_with_tag_invoke<
+ next_sender_of_t<_Receiver, _Sender>&&,
+ __stopped_means_break_t<_Receiver>>;
+
+template <class _Sender, class _Receiver>
+concept __subscribeable_with_tag_invoke =
+ receiver<_Receiver> && //
+ sequence_sender_in<_Sender, env_of_t<_Receiver>> && //
+ sequence_receiver_from<_Receiver, _Sender> && //
+ tag_invocable<subscribe_t, _Sender, _Receiver>;
+
+struct subscribe_t
+{
+ template <class _Sender, class _Receiver>
+ using __tfx_sndr = __tfx_sender<_Sender, env_of_t<_Receiver>>;
+
+ template <class _Sender, class _Receiver>
+ static constexpr auto __select_impl() noexcept
+ {
+ using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver&>>;
+ constexpr bool _NothrowTfxSender =
+ __nothrow_callable<get_env_t, _Receiver&> &&
+ __nothrow_callable<transform_sender_t, _Domain, _Sender,
+ env_of_t<_Receiver&>>;
+ using _TfxSender = __tfx_sndr<_Sender, _Receiver>;
+ if constexpr (__next_connectable_with_tag_invoke<_TfxSender, _Receiver>)
+ {
+ using _Result =
+ tag_invoke_result_t<connect_t,
+ next_sender_of_t<_Receiver, _TfxSender>,
+ __stopped_means_break_t<_Receiver>>;
+ constexpr bool _Nothrow =
+ nothrow_tag_invocable<connect_t,
+ next_sender_of_t<_Receiver, _TfxSender>,
+ __stopped_means_break_t<_Receiver>>;
+ return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
+ }
+ else if constexpr (__subscribeable_with_tag_invoke<_TfxSender,
+ _Receiver>)
+ {
+ using _Result =
+ tag_invoke_result_t<subscribe_t, _TfxSender, _Receiver>;
+ constexpr bool _Nothrow = //
+ _NothrowTfxSender &&
+ nothrow_tag_invocable<subscribe_t, _TfxSender, _Receiver>;
+ return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
+ }
+ else
+ {
+ return static_cast<__debug::__debug_operation (*)() noexcept>(
+ nullptr);
+ }
+ }
+
+ template <class _Sender, class _Receiver>
+ using __select_impl_t = decltype(__select_impl<_Sender, _Receiver>());
+
+ template <sender _Sender, receiver _Receiver>
+ requires __next_connectable_with_tag_invoke<
+ __tfx_sndr<_Sender, _Receiver>, _Receiver> ||
+ __subscribeable_with_tag_invoke<__tfx_sndr<_Sender, _Receiver>,
+ _Receiver> ||
+ __is_debug_env<env_of_t<_Receiver>>
+ auto operator()(_Sender&& __sndr, _Receiver&& __rcvr) const
+ noexcept(__nothrow_callable<__select_impl_t<_Sender, _Receiver>>)
+ -> __call_result_t<__select_impl_t<_Sender, _Receiver>>
+ {
+ using _TfxSender = __tfx_sndr<_Sender, _Receiver>;
+ auto&& __env = get_env(__rcvr);
+ auto __domain = __get_late_domain(__sndr, __env);
+ if constexpr (__next_connectable_with_tag_invoke<_TfxSender, _Receiver>)
+ {
+ static_assert(
+ operation_state<tag_invoke_result_t<
+ connect_t, next_sender_of_t<_Receiver, _TfxSender>,
+ __stopped_means_break_t<_Receiver>>>,
+ "stdexec::connect(sender, receiver) must return a type that "
+ "satisfies the operation_state concept");
+ next_sender_of_t<_Receiver, _TfxSender> __next = set_next(
+ __rcvr, transform_sender(__domain, (_Sender&&)__sndr, __env));
+ return tag_invoke(
+ connect_t{},
+ static_cast<next_sender_of_t<_Receiver, _TfxSender>&&>(__next),
+ __stopped_means_break_t<_Receiver>{(_Receiver&&)__rcvr});
+ }
+ else if constexpr (__subscribeable_with_tag_invoke<_TfxSender,
+ _Receiver>)
+ {
+ static_assert(
+ operation_state<
+ tag_invoke_result_t<subscribe_t, _TfxSender, _Receiver>>,
+ "exec::subscribe(sender, receiver) must return a type that "
+ "satisfies the operation_state concept");
+ return tag_invoke(
+ subscribe_t{},
+ transform_sender(__domain, (_Sender&&)__sndr, __env),
+ (_Receiver&&)__rcvr);
+ }
+ else if constexpr (enable_sequence_sender<
+ stdexec::__decay_t<_TfxSender>>)
+ {
+ // This should generate an instantiate backtrace that contains
+ // useful debugging information.
+ using __tag_invoke::tag_invoke;
+ tag_invoke(*this,
+ transform_sender(__domain, (_Sender&&)__sndr, __env),
+ (_Receiver&&)__rcvr);
+ }
+ else
+ {
+ next_sender_of_t<_Receiver, _TfxSender> __next = set_next(
+ __rcvr, transform_sender(__domain, (_Sender&&)__sndr, __env));
+ return tag_invoke(
+ connect_t{},
+ static_cast<next_sender_of_t<_Receiver, _TfxSender>&&>(__next),
+ __stopped_means_break_t<_Receiver>{(_Receiver&&)__rcvr});
+ }
+ }
+
+ friend constexpr bool tag_invoke(forwarding_query_t, subscribe_t) noexcept
+ {
+ return false;
+ }
+};
+
+template <class _Sender, class _Receiver>
+using subscribe_result_t = __call_result_t<subscribe_t, _Sender, _Receiver>;
+} // namespace __sequence_sndr
+
+using __sequence_sndr::__single_sender_completion_sigs;
+
+using __sequence_sndr::subscribe_t;
+inline constexpr subscribe_t subscribe;
+
+using __sequence_sndr::subscribe_result_t;
+
+template <class _Sender, class _Receiver>
+concept sequence_sender_to =
+ sequence_receiver_from<_Receiver, _Sender> && //
+ requires(_Sender&& __sndr, _Receiver&& __rcvr) {
+ {
+ subscribe((_Sender&&)__sndr, (_Receiver&&)__rcvr)
+ };
+ };
+
+template <class _Receiver>
+concept __stoppable_receiver = //
+ stdexec::__callable<stdexec::set_value_t, _Receiver> && //
+ (stdexec::unstoppable_token<
+ stdexec::stop_token_of_t<stdexec::env_of_t<_Receiver>>> ||
+ stdexec::__callable<stdexec::set_stopped_t, _Receiver>);
+
+template <class _Receiver>
+ requires __stoppable_receiver<_Receiver>
+void __set_value_unless_stopped(_Receiver&& __rcvr)
+{
+ using token_type = stdexec::stop_token_of_t<stdexec::env_of_t<_Receiver>>;
+ if constexpr (stdexec::unstoppable_token<token_type>)
+ {
+ stdexec::set_value(static_cast<_Receiver&&>(__rcvr));
+ }
+ else
+ {
+ auto token = stdexec::get_stop_token(stdexec::get_env(__rcvr));
+ if (!token.stop_requested())
+ {
+ stdexec::set_value(static_cast<_Receiver&&>(__rcvr));
+ }
+ else
+ {
+ stdexec::set_stopped(static_cast<_Receiver&&>(__rcvr));
+ }
+ }
+}
+} // namespace exec
diff --git a/include/sdbusplus/async/stdexec/stop_token.hpp b/include/sdbusplus/async/stdexec/stop_token.hpp
index 28ca42a..e23ee1f 100644
--- a/include/sdbusplus/async/stdexec/stop_token.hpp
+++ b/include/sdbusplus/async/stdexec/stop_token.hpp
@@ -261,7 +261,7 @@
std::move(static_cast<in_place_stop_callback*>(cb)->__fun_)();
}
- STDEXEC_NO_UNIQUE_ADDRESS _Fun __fun_;
+ STDEXEC_ATTRIBUTE((no_unique_address)) _Fun __fun_;
};
namespace __stok
diff --git a/include/sdbusplus/async/stdexec/task.hpp b/include/sdbusplus/async/stdexec/task.hpp
index c2da093..379941e 100644
--- a/include/sdbusplus/async/stdexec/task.hpp
+++ b/include/sdbusplus/async/stdexec/task.hpp
@@ -30,7 +30,8 @@
#include <variant>
STDEXEC_PRAGMA_PUSH()
-STDEXEC_PRAGMA_IGNORE("-Wundefined-inline")
+STDEXEC_PRAGMA_IGNORE_GNU("-Wpragmas")
+STDEXEC_PRAGMA_IGNORE_GNU("-Wundefined-inline")
namespace exec
{
@@ -107,7 +108,7 @@
static constexpr bool __with_scheduler = _SchedulerAffinity ==
__scheduler_affinity::__sticky;
- STDEXEC_NO_UNIQUE_ADDRESS
+ STDEXEC_ATTRIBUTE((no_unique_address))
__if_c<__with_scheduler, __any_scheduler, __ignore> //
__scheduler_{exec::inline_scheduler{}};
in_place_stop_token __stop_token_;
@@ -389,9 +390,9 @@
private:
struct __final_awaitable
{
- static std::false_type await_ready() noexcept
+ static constexpr bool await_ready() noexcept
{
- return {};
+ return false;
}
static __coro::coroutine_handle<>
@@ -499,9 +500,9 @@
__coro_.destroy();
}
- static std::false_type await_ready() noexcept
+ static constexpr bool await_ready() noexcept
{
- return {};
+ return false;
}
template <class _ParentPromise2>
@@ -550,7 +551,7 @@
// Make this task generally awaitable:
friend __task_awaitable<> operator co_await(basic_task && __self) noexcept
- requires __valid<awaiter_context_t, __promise>
+ requires __mvalid<awaiter_context_t, __promise>
{
return __task_awaitable<>{std::exchange(__self.__coro_, {})};
}
@@ -569,7 +570,10 @@
set_error_t(std::exception_ptr), set_stopped_t()>;
friend auto tag_invoke(get_completion_signatures_t, const basic_task&, auto)
- -> __task_traits_t;
+ -> __task_traits_t
+ {
+ return {};
+ }
explicit basic_task(__coro::coroutine_handle<promise_type> __coro) noexcept
: