let_value does not propagate nothrow-connect-ness
Opened this issue · 2 comments
Consider this code:
#include <stdexec/execution.hpp>
void test() {
auto s1 = stdexec::let_value(
stdexec::just(),
[]() noexcept {
return stdexec::just();
}
);
static_assert(stdexec::__detail::__connectable<decltype(s1), stdexec::__sync_wait::__receiver_t<decltype(s1)>>);
static_assert(stdexec::__nothrow_connectable<decltype(s1), stdexec::__sync_wait::__receiver_t<decltype(s1)>>); // <
}
(Godbolt link: https://godbolt.org/z/hn5MG7dqe)
Compilation fails on the line marked with <
, which suggests than s1
is a valid sender, but its connect
CPO is declared as throwing.
This is surprising: as fas as I can see, there is no room for an exception in this code, since just()
returns nothrow-connectable sender, and lambda itself is declared as non-throwing. let_value
internal implementation is hard to follow (in particular, while reading [exec]
I wasn't able to find what domain really is), so I can't pinpoint source of confusion any further.
Is this behavior intended?
Looks like there is perhaps a missing noexcept declaration on let_value
's get_state
impls function.
e.g. see here
stdexec/include/stdexec/__detail/__let.hpp
Line 459 in 999a11e
Note that there is also another issue that can potentially cause sync_wait
of let_value
to be potentially-throwing, which is a currentl limitation of the env-based completion-signature computation.
At the time that let_value
is computing the completion-signatures, it only has access to the environment type, which means it cannot generally compute the exact receiver type that will be passed to connect()
on the second sender returned by the lambda, and so it cannot reliably determine if connect()
will be noexcept or not. In some cases this can mean it conservatively needs to assume it might throw and thus will add set_error_t(std::exception_ptr)
to the completion-signatures.