Skip to content

Commit a0bc7a5

Browse files
committed
Merge remote-tracking branch 'origin/main' into repeat_until_20200221
2 parents 3d97de3 + d819022 commit a0bc7a5

File tree

4 files changed

+192
-139
lines changed

4 files changed

+192
-139
lines changed

include/exec/sequence.hpp

Lines changed: 87 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
#include "../stdexec/__detail/__variant.hpp"
2020
#include "../stdexec/execution.hpp"
2121

22+
#include "completion_signatures.hpp"
23+
24+
#include <type_traits>
25+
2226
STDEXEC_PRAGMA_PUSH()
2327
STDEXEC_PRAGMA_IGNORE_GNU("-Wmissing-braces")
2428

@@ -54,18 +58,11 @@ namespace experimental::execution
5458
STDEXEC_ATTRIBUTE(host, device)
5559
constexpr void _start_next() noexcept
5660
{
57-
STDEXEC_TRY
58-
{
59-
(*_start_next_)(this);
60-
}
61-
STDEXEC_CATCH_ALL
62-
{
63-
STDEXEC::set_error(static_cast<Rcvr&&>(_rcvr), std::current_exception());
64-
}
61+
(*_start_next_)(this);
6562
}
6663

6764
Rcvr _rcvr;
68-
void (*_start_next_)(_opstate_base*) = nullptr;
65+
void (*_start_next_)(_opstate_base*) noexcept = nullptr;
6966
};
7067

7168
template <class Rcvr>
@@ -120,15 +117,18 @@ namespace experimental::execution
120117
{
121118
template <class... _Ts>
122119
STDEXEC_ATTRIBUTE(host, device, always_inline)
123-
constexpr auto
124-
operator()(_Ts&&... __ts) const STDEXEC_AUTO_RETURN(_Tuple{static_cast<_Ts&&>(__ts)...});
120+
constexpr _Tuple operator()(_Ts&&... __ts) const
121+
noexcept(STDEXEC::__nothrow_constructible_from<_Tuple, _Ts...>)
122+
{
123+
return _Tuple{static_cast<_Ts&&>(__ts)...};
124+
}
125125
};
126126

127127
template <class Rcvr, class... Senders>
128128
struct _opstate;
129129

130-
template <class Rcvr, class Sender0, class... Senders>
131-
struct _opstate<Rcvr, Sender0, Senders...> : _opstate_base<Rcvr>
130+
template <class Rcvr, class CvSender0, class... Senders>
131+
struct _opstate<Rcvr, CvSender0, Senders...> : _opstate_base<Rcvr>
132132
{
133133
using operation_state_concept = STDEXEC::operation_state_t;
134134

@@ -146,20 +146,25 @@ namespace experimental::execution
146146
using _mk_child_ops_variant_fn =
147147
STDEXEC::__mzip_with2<STDEXEC::__q2<_child_opstate_t>, STDEXEC::__qq<STDEXEC::__variant>>;
148148

149-
using _ops_variant_t = STDEXEC::__minvoke<
150-
_mk_child_ops_variant_fn,
151-
STDEXEC::__tuple<Sender0, Senders...>,
149+
using __is_last_mask_t =
152150
STDEXEC::__mfill_c<sizeof...(Senders),
153151
STDEXEC::__mfalse,
154-
STDEXEC::__mbind_back_q<STDEXEC::__mlist, STDEXEC::__mtrue>>>;
152+
STDEXEC::__mbind_back_q<STDEXEC::__mlist, STDEXEC::__mtrue>>;
153+
154+
using _ops_variant_t = STDEXEC::__minvoke<_mk_child_ops_variant_fn,
155+
STDEXEC::__tuple<CvSender0, Senders...>,
156+
__is_last_mask_t>;
155157

156158
template <class CvSndrs>
157159
STDEXEC_ATTRIBUTE(host, device)
158160
constexpr explicit _opstate(Rcvr&& rcvr, CvSndrs&& sndrs)
161+
noexcept(::STDEXEC::__nothrow_applicable<__convert_tuple_fn<_senders_tuple_t>, CvSndrs>
162+
&& ::STDEXEC::__nothrow_connectable<::STDEXEC::__tuple_element_t<0, CvSndrs>,
163+
_rcvr_t<sizeof...(Senders) == 0>>)
159164
: _opstate_base<Rcvr>{static_cast<Rcvr&&>(rcvr)}
165+
// move all but the first sender into the opstate:
160166
, _sndrs{
161167
STDEXEC::__apply(__convert_tuple_fn<_senders_tuple_t>{}, static_cast<CvSndrs&&>(sndrs))}
162-
// move all but the first sender into the opstate.
163168
{
164169
// Below, it looks like we are using `sndrs` after it has been moved from. This is not the
165170
// case. `sndrs` is moved into a tuple type that has `__ignore` for the first element. The
@@ -170,19 +175,37 @@ namespace experimental::execution
170175
}
171176

172177
template <std::size_t Remaining>
173-
static constexpr void _start_next(_opstate_base<Rcvr>* _self)
178+
static constexpr void _start_next(_opstate_base<Rcvr>* _self) noexcept
174179
{
175180
constexpr auto __nth = sizeof...(Senders) - Remaining;
176181
auto* self = static_cast<_opstate*>(_self);
177182
auto& sndr = STDEXEC::__get<__nth + 1>(self->_sndrs);
178-
auto& op = self->_ops.template __emplace_from<__nth + 1>(STDEXEC::connect,
179-
std::move(sndr),
180-
_rcvr_t<Remaining == 1>{self});
181-
if constexpr (Remaining > 1)
183+
constexpr bool nothrow =
184+
STDEXEC::__nothrow_connectable<STDEXEC::__m_at_c<__nth, Senders...>,
185+
_rcvr_t<Remaining == 1>>;
186+
STDEXEC_TRY
187+
{
188+
auto& op = self->_ops.template __emplace_from<__nth + 1>(STDEXEC::connect,
189+
std::move(sndr),
190+
_rcvr_t<Remaining == 1>{self});
191+
if constexpr (Remaining > 1)
192+
{
193+
self->_start_next_ = &_start_next<Remaining - 1>;
194+
}
195+
STDEXEC::start(op);
196+
}
197+
STDEXEC_CATCH_ALL
182198
{
183-
self->_start_next_ = &_start_next<Remaining - 1>;
199+
if constexpr (nothrow)
200+
{
201+
STDEXEC::__std::unreachable();
202+
}
203+
else
204+
{
205+
STDEXEC::set_error(static_cast<Rcvr&&>(static_cast<_opstate*>(_self)->_rcvr),
206+
std::current_exception());
207+
}
184208
}
185-
STDEXEC::start(op);
186209
}
187210

188211
STDEXEC_ATTRIBUTE(host, device)
@@ -199,70 +222,65 @@ namespace experimental::execution
199222
_ops_variant_t _ops{STDEXEC::__no_init};
200223
};
201224

202-
// The completions of the sequence sender are the error and stopped completions of all the
203-
// child senders plus the value completions of the last child sender.
204-
template <class... Env>
205-
struct _completions_fn
206-
{
207-
// When folding left, the first sender folded will be the last sender in the list. That is
208-
// also when the "state" of the fold is void. For this case we want to include the value
209-
// completions; otherwise, we want to exclude them.
210-
template <class State, class... Args>
211-
struct _fold_left;
212-
213-
template <class State, class Head, class... Tail>
214-
struct _fold_left<State, Head, Tail...>
215-
{
216-
using __t = STDEXEC::__gather_completion_signatures_t<
217-
STDEXEC::__completion_signatures_of_t<Head, Env...>,
218-
STDEXEC::set_value_t,
219-
STDEXEC::__mconst<STDEXEC::completion_signatures<>>::__f,
220-
STDEXEC::__cmplsigs::__default_completion,
221-
STDEXEC::__mtry_q<STDEXEC::__concat_completion_signatures_t>::__f,
222-
STDEXEC::__t<_fold_left<State, Tail...>>>;
223-
};
224-
225-
template <class Head>
226-
struct _fold_left<void, Head>
227-
{
228-
using __t = STDEXEC::__mtry_q<STDEXEC::__concat_completion_signatures_t>::__f<
229-
STDEXEC::completion_signatures<STDEXEC::set_error_t(std::exception_ptr)>,
230-
STDEXEC::__completion_signatures_of_t<Head, Env...>>;
231-
};
232-
233-
template <class... Sender>
234-
using __f = STDEXEC::__t<_fold_left<void, Sender...>>;
235-
};
225+
template <class Sender>
226+
concept __has_eptr_completion =
227+
STDEXEC::sender_in<Sender>
228+
&& exec::transform_completion_signatures(STDEXEC::get_completion_signatures<Sender>(),
229+
exec::ignore_completion(),
230+
exec::decay_arguments<STDEXEC::set_error_t>(),
231+
exec::ignore_completion())
232+
.__contains(STDEXEC::__fn_ptr_t<STDEXEC::set_error_t, std::exception_ptr>());
236233

237234
template <class Sender0, class... Senders>
238235
struct _sndr<Sender0, Senders...>
239236
{
240237
using sender_concept = STDEXEC::sender_t;
241238

239+
// Even without an Env, we can sometimes still determine the completion signatures
240+
// of the sequence sender. If any of the child senders has a
241+
// set_error(exception_ptr) completion, then the sequence sender has a
242+
// set_error(exception_ptr) completion. We don't have to ask if any connect call
243+
// throws.
242244
template <class Self, class... Env>
243-
using _completions_t = STDEXEC::__minvoke<_completions_fn<Env...>,
244-
STDEXEC::__copy_cvref_t<Self, Sender0>,
245-
Senders...>;
246-
247-
template <class Self, class... Env>
245+
requires(sizeof...(Env) > 0)
246+
|| __has_eptr_completion<STDEXEC::__copy_cvref_t<Self, Sender0>>
247+
|| (__has_eptr_completion<Senders> || ...)
248248
STDEXEC_ATTRIBUTE(host, device)
249249
static consteval auto get_completion_signatures()
250250
{
251-
if constexpr (STDEXEC::__decay_copyable<Self>)
252-
{
253-
return _completions_t<Self, Env...>{};
254-
}
255-
else
251+
if constexpr (!STDEXEC::__decay_copyable<Self>)
256252
{
257253
return STDEXEC::__throw_compile_time_error<
258254
STDEXEC::_SENDER_TYPE_IS_NOT_DECAY_COPYABLE_,
259255
STDEXEC::_WITH_PRETTY_SENDER_<_sndr<Sender0, Senders...>>>();
260256
}
257+
else
258+
{
259+
using __env_t = STDEXEC::__mfront<Env..., STDEXEC::env<>>;
260+
using __rcvr_t = STDEXEC::__receiver_archetype<__env_t>;
261+
constexpr bool nothrow = (STDEXEC::__nothrow_connectable<Senders, __rcvr_t> && ...);
262+
263+
// The completions of the sequence sender are the error and stopped completions of all the
264+
// child senders plus the value completions of the last child sender.
265+
return exec::concat_completion_signatures(
266+
exec::transform_completion_signatures(
267+
STDEXEC::get_completion_signatures<STDEXEC::__copy_cvref_t<Self, Sender0>, Env...>(),
268+
exec::ignore_completion()),
269+
exec::transform_completion_signatures(
270+
STDEXEC::get_completion_signatures<Senders, Env...>(),
271+
exec::ignore_completion())...,
272+
STDEXEC::get_completion_signatures<STDEXEC::__mback<Senders...>, Env...>(),
273+
STDEXEC::__eptr_completion_unless<nothrow>());
274+
}
261275
}
262276

263277
template <STDEXEC::__decay_copyable Self, class Rcvr>
264278
STDEXEC_ATTRIBUTE(host, device)
265279
constexpr STDEXEC_EXPLICIT_THIS_BEGIN(auto connect)(this Self&& self, Rcvr rcvr)
280+
noexcept(STDEXEC::__nothrow_constructible_from<
281+
_opstate<Rcvr, STDEXEC::__copy_cvref_t<Self, Sender0>, Senders...>,
282+
Rcvr,
283+
decltype((static_cast<Self&&>(self)._sndrs))>)
266284
{
267285
return _opstate<Rcvr, STDEXEC::__copy_cvref_t<Self, Sender0>, Senders...>{
268286
static_cast<Rcvr&&>(rcvr),

0 commit comments

Comments
 (0)