This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of LEWG status.
awaitable-receiver::set_value should use Mandates instead of constraintsSection: 33.13.1 [exec.as.awaitable] Status: LEWG Submitter: Lewis Baker Opened: 2025-08-28 Last modified: 2025-10-23
Priority: 1
View other active issues in [exec.as.awaitable].
View all other issues in [exec.as.awaitable].
View all issues with LEWG status.
Discussion:
In 33.13.1 [exec.as.awaitable] bullet 4.1 the awaitable-receiver::set_value member function
is defined as having a constraint that the result-type is constructible from the values.
If
constructible_from<result-type, decltype((vs))...>is satisfied, the expressionset_value(rcvr, vs...)is equivalent to:try { rcvr.result-ptr->template emplace<1>(vs...); } catch(...) { rcvr.result-ptr->template emplace<2>(current_exception()); } rcvr.continuation.resume();Otherwise,
set_value(rcvr, vs...)is ill-formed.
Should we be using mandates here instead of constraints (or alternatively just drop the constraint altogether)? There shouldn't be any need to change behaviour based on whether or not the receiver's completion methods are well-formed or not.
It is worth noting that there is inconsistent use of constraints onset_value methods in other receiver
implementations throughout 33 [exec].
For example: The following set_value member function applies constraints:
In 33.9.2 [exec.snd.expos] basic-receiver::set_value constrains that check that it can accept those specific value arguments
While the following set_value member functions do not apply constraints:
In 33.9.12.10 [exec.let] receiver2::set_value
In 33.9.12.18 [exec.spawn.future] spawn-future-receiver::set_value
in 33.9.13.1 [exec.sync.wait] sync-wait-receiver::set_value
We should probably try to be consistent on whether or not set_value implementations
should use constraints or mandates. Given that it is not allowed to form calls to the
receiver unless that overload is present in the completion_signatures, it may be worth
just making them all mandates. This would tend to make uses of the receiver_of concept
less useful as satisfying receiver_of<R, Sig> would not necessarily
guarantee that actually trying to call each of R's corresponding completion functions
will result in a well-formed program. It is arguable that this is already the status-quo, however.
[2025-10-23; Reflector poll. Status changed: New → LEWG]
Set priority to 1 after reflector poll.
"Send to LEWG if we want to go this way, which makes receiver_of mostly meaningless
and calls into question why we even have the concept."
Proposed resolution:
This wording is relative to N5014.
Modify 33.13.1 [exec.as.awaitable] as indicated:
-4- Let
rcvrbe an rvalue expression of typeawaitable-receiver, letcrcvrbe a const lvalue that refers torcvr, letvsbe a pack of subexpressions, and leterrbe an expression of typeErr. Then:
(4.1) —
IfThe expressionconstructible_from<result-type, decltype((vs))...>is satisfied, tset_value(rcvr, vs...)is equivalent to:try { rcvr.result-ptr->template emplace<1>(vs...); } catch(...) { rcvr.result-ptr->template emplace<2>(current_exception()); } rcvr.continuation.resume();
Otherwise,Mandates:set_value(rcvr, vs...)is ill-formedconstructible_from<result-type, decltype((vs))...>is satisfied.(4.2) — […]
(4.3) — […]
(4.4) — […]
Modify 33.9.2 [exec.snd.expos] after p25 as indicated:
[…]
template<class Sndr, class Rcvr, class Index>
requires valid-specialization<env-type, Index, Sndr, Rcvr>
struct basic-receiver { // exposition only
using receiver_concept = receiver_t;
using tag-t = tag_of_t<Sndr>; // exposition only
using state-t = state-type<Sndr, Rcvr>; // exposition only
static constexpr const auto& complete = impls-for<tag-t>::complete; // exposition only
template<class... Args>
requires callable<decltype(complete), Index, state-t&, Rcvr&, set_value_t, Args...>
void set_value(Args&&... args) && noexcept {
complete(Index(), op->state, op->rcvr, set_value_t(), std::forward<Args>(args)...);
}
template<class Error>
requires callable<decltype(complete), Index, state-t&, Rcvr&, set_error_t, Error>
void set_error(Error&& err) && noexcept {
complete(Index(), op->state, op->rcvr, set_error_t(), std::forward<Error>(err));
}
void set_stopped() && noexcept
requires callable<decltype(complete), Index, state-t&, Rcvr&, set_stopped_t> {
complete(Index(), op->state, op->rcvr, set_stopped_t());
}
auto get_env() const noexcept -> env-type<Index, Sndr, Rcvr> {
return impls-for<tag-t>::get-env(Index(), op->state, op->rcvr);
}
basic-state<Sndr, Rcvr>* op; // exposition only
};
[…]