This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
awaitable-receiver::set_value
should use Mandates instead of constraintsSection: 33.13.1 [exec.as.awaitable] Status: New Submitter: Lewis Baker Opened: 2025-08-28 Last modified: 2025-09-14
Priority: Not Prioritized
View other active issues in [exec.as.awaitable].
View all other issues in [exec.as.awaitable].
View all issues with New 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.
Proposed resolution:
This wording is relative to N5014.
Modify 33.13.1 [exec.as.awaitable] as indicated:
-4- Let
rcvr
be an rvalue expression of typeawaitable-receiver
, letcrcvr
be a const lvalue that refers torcvr
, letvs
be a pack of subexpressions, and leterr
be 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() && noexceptrequires 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 }; […]