Skip to content

Commit e721274

Browse files
committed
New issue from Lewis Baker: "awaitable-receiver::set_value should use Mandates instead of constraints"
1 parent 473bca1 commit e721274

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

xml/issue4361.xml

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4361" status="New">
5+
<title><tt><i>awaitable-receiver</i>::set_value</tt> should use <i>Mandates</i> instead of constraints</title>
6+
<section>
7+
<sref ref="[exec.as.awaitable]"/>
8+
</section>
9+
<submitter>Lewis Baker</submitter>
10+
<date>28 Aug 2025</date>
11+
<priority>99</priority>
12+
13+
<discussion>
14+
<p>
15+
In <sref ref="[exec.as.awaitable]"/> bullet 4.1 the <tt><i>awaitable-receiver</i>::set_value</tt> member function
16+
is defined as having a constraint that the <tt><i>result-type</i></tt> is constructible from the values.
17+
</p>
18+
<blockquote>
19+
<p>
20+
If <tt>constructible_from&lt;<i>result-type</i>, decltype((vs))...&gt;</tt> is satisfied, the expression
21+
`set_value(rcvr, vs...)` is equivalent to:
22+
</p>
23+
<blockquote><pre>
24+
try {
25+
rcvr.<i>result-ptr</i>-&gt;template emplace&lt;1&gt;(vs...);
26+
} catch(...) {
27+
rcvr.<i>result-ptr</i>-&gt;template emplace&lt;2&gt;(current_exception());
28+
}
29+
rcvr.<i>continuation</i>.resume();
30+
</pre></blockquote>
31+
<p>
32+
Otherwise, `set_value(rcvr, vs...)` is ill-formed.
33+
</p>
34+
</blockquote>
35+
<p>
36+
Should we be using mandates here instead of constraints (or alternatively just drop the constraint altogether)?
37+
There shouldn't be any need to change behaviour based on whether or not the receiver's completion methods
38+
are well-formed or not.
39+
<p/>
40+
It is worth noting that there is inconsistent use of constraints on `set_value` methods in other receiver
41+
implementations throughout <sref ref="[exec]"/>.
42+
<p/>
43+
For example: The following `set_value` member function applies constraints:
44+
</p>
45+
<ul>
46+
<li><p>In <sref ref="[exec.snd.expos]"/> <tt><i>basic-receiver</i>::set_value</tt> constrains that check that it can accept those specific value arguments</p></li>
47+
</ul>
48+
<p>
49+
While the following `set_value` member functions do not apply constraints:
50+
</p>
51+
<ul>
52+
<li><p>In <sref ref="[exec.let]"/> <tt><i>receiver2</i>::set_value</tt></p></li>
53+
<li><p>In <sref ref="[exec.spawn.future]"/> <tt><i>spawn-future-receiver</i>::set_value</tt></p></li>
54+
<li><p>in <sref ref="[exec.sync.wait]"/> <tt><i>sync-wait-receiver</i>::set_value</tt></p></li>
55+
</ul>
56+
<p>
57+
We should probably try to be consistent on whether or not `set_value` implementations
58+
should use constraints or mandates. Given that it is not allowed to form calls to the
59+
receiver unless that overload is present in the `completion_signatures`, it may be worth
60+
just making them all mandates. This would tend to make uses of the `receiver_of` concept
61+
less useful as satisfying <tt>receiver_of&lt;R, Sig&gt;</tt> would not necessarily
62+
guarantee that actually trying to call each of `R`'s corresponding completion functions
63+
will result in a well-formed program. It is arguable that this is already the status-quo, however.
64+
</p>
65+
</discussion>
66+
67+
<resolution>
68+
<p>
69+
This wording is relative to <paper num="N5014"/>.
70+
</p>
71+
72+
<ol>
73+
74+
<li><p>Modify <sref ref="[exec.as.awaitable]"/> as indicated:</p>
75+
76+
<blockquote>
77+
<p>
78+
-4- Let `rcvr` be an rvalue expression of type <tt><i>awaitable-receiver</i></tt>, let
79+
`crcvr` be a const lvalue that refers to `rcvr`, let `vs` be a pack of subexpressions,
80+
and let `err` be an expression of type `Err`. Then:
81+
</p>
82+
<ol style="list-style-type: none">
83+
<li><p>(4.1) &mdash; <del>If <tt>constructible_from&lt;<i>result-type</i>, decltype((vs))...&gt;</tt> is satisfied,
84+
t</del><ins>T</ins>he expression `set_value(rcvr, vs...)` is equivalent to:
85+
</p>
86+
<blockquote><pre>
87+
try {
88+
rcvr.<i>result-ptr</i>-&gt;template emplace&lt;1&gt;(vs...);
89+
} catch(...) {
90+
rcvr.<i>result-ptr</i>-&gt;template emplace&lt;2&gt;(current_exception());
91+
}
92+
rcvr.<i>continuation</i>.resume();
93+
</pre></blockquote>
94+
<p>
95+
<del>Otherwise, `set_value(rcvr, vs...)` is ill-formed</del><ins><i>Mandates:</i>
96+
<tt>constructible_from&lt;<i>result-type</i>, decltype((vs))...&gt;</tt> is satisfied</ins>.</p></li>
97+
<li><p>(4.2) &mdash; [&hellip;]</p></li>
98+
<li><p>(4.3) &mdash; [&hellip;]</p></li>
99+
<li><p>(4.4) &mdash; [&hellip;]</p></li>
100+
</ol>
101+
</blockquote>
102+
103+
</li>
104+
105+
106+
<li><p>Modify <sref ref="[exec.snd.expos]"/> after p25 as indicated:</p>
107+
108+
<blockquote><pre>
109+
[&hellip;]
110+
template&lt;class Sndr, class Rcvr, class Index&gt;
111+
requires <i>valid-specialization</i>&lt;<i>env-type</i>, Index, Sndr, Rcvr&gt;
112+
struct <i>basic-receiver</i> { // <i>exposition only</i>
113+
using receiver_concept = receiver_t;
114+
115+
using <i>tag-t</i> = tag_of_t&lt;Sndr&gt;; // <i>exposition only</i>
116+
using <i>state-t</i> = <i>state-type</i>&lt;Sndr, Rcvr&gt;; // <i>exposition only</i>
117+
static constexpr const auto&amp; <i>complete</i> = <i>impls-for</i>&lt;<i>tag-t</i>&gt;::<i>complete</i>; // exposition only
118+
119+
template&lt;class... Args&gt;
120+
<del>requires <i>callable</i>&lt;decltype(<i>complete</i>), Index, <i>state-t</i>&amp;, Rcvr&amp;, set_value_t, Args...&gt;</del>
121+
void set_value(Args&amp;&amp;... args) &amp;&amp; noexcept {
122+
<i>complete</i>(Index(), op-&gt;<i>state</i>, op-&gt;<i>rcvr</i>, set_value_t(), std::forward&lt;Args&gt;(args)...);
123+
}
124+
125+
template&lt;class Error&gt;
126+
<del>requires <i>callable</i>&lt;decltype(<i>complete</i>), Index, <i>state-t</i>&amp;, Rcvr&amp;, set_error_t, Error&gt;</del>
127+
void set_error(Error&amp;&amp; err) &amp;&amp; noexcept {
128+
<i>complete</i>(Index(), op-&gt;<i>state</i>, op-&gt;<i>rcvr</i>, set_error_t(), std::forward&lt;Error&gt;(err));
129+
}
130+
131+
void set_stopped() &amp;&amp; noexcept
132+
<del>requires <i>callable</i>&lt;decltype(<i>complete</i>), Index, <i>state-t</i>&amp;, Rcvr&amp;, set_stopped_t&gt;</del> {
133+
<i>complete</i>(Index(), op-&gt;<i>state</i>, op-&gt;<i>rcvr</i>, set_stopped_t());
134+
}
135+
136+
auto get_env() const noexcept -&gt; <i>env-type</i>&lt;Index, Sndr, Rcvr&gt; {
137+
return <i>impls-for</i>&lt;tag-t&gt;::<i>get-env</i>(Index(), op-&gt;<i>state</i>, op-&gt;<i>rcvr</i>);
138+
}
139+
140+
<i>basic-state</i>&lt;Sndr, Rcvr&gt;* <i>op</i>; // <i>exposition only</i>
141+
};
142+
[&hellip;]
143+
</pre></blockquote>
144+
145+
</li>
146+
147+
</ol>
148+
</resolution>
149+
150+
</issue>

0 commit comments

Comments
 (0)