Skip to content

Commit a2eebee

Browse files
committed
New issue from Lewis Baker: "Uses of MANDATE-NOTHROW in CPOs should not enclose CPO argument sub-expressions"
1 parent c870388 commit a2eebee

File tree

1 file changed

+96
-0
lines changed

1 file changed

+96
-0
lines changed

xml/issue4353.xml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4353" status="New">
5+
<title>Uses of <tt><i>MANDATE-NOTHROW</i></tt> in CPOs should not enclose CPO argument sub-expressions</title>
6+
<section>
7+
<sref ref="[exec]"/>
8+
</section>
9+
<submitter>Lewis Baker</submitter>
10+
<date>25 Aug 2025</date>
11+
<priority>99</priority>
12+
13+
<discussion>
14+
<p>
15+
There are a number of CPOs defined in <sref ref="[exec]"/> which have behaviour specified in terms of being
16+
expression-equivalent to a <tt><i>MANDATE-NOTHROW</i></tt> expression.
17+
<p/>
18+
The intent of this is that we want to make sure that the call that the CPO dispatches to is marked `noexcept`.
19+
<p/>
20+
However, the way that these CPOs are currently specified in terms of sub-expressions means that we are currently
21+
requiring that all of the expressions passed as arguments to the CPO are also `noexcept`. Outside of defining
22+
these CPOs as preprocessor macros, this is unimplementable &mdash; and also undesirable behaviour.
23+
<p/>
24+
For example, <sref ref="[exec.set.value]"/> defines `set_value(rcvr, vs...)` to be equivalent to
25+
<tt><i>MANDATE-NOTHROW</i>(rcvr.set_value(vs...))</tt> for sub-expressions `rcvr` and pack of sub-expressions
26+
`vs`.
27+
<p/>
28+
In <sref ref="[exec.general]"/> p5 we define <tt><i>MANDATE-NOTHROW</i>(expr)</tt> as expression-equivalent to
29+
`expr` but mandate that `noexcept(expr)` is `true`.
30+
<p/>
31+
So in the above definition of `set_value(rcvr, vs...)` we are actually requiring that the expression
32+
`noexcept(rcvr.set_value(vs...))` is `true`.
33+
<p/>
34+
This is only true if all of the sub-expressions are `noexcept`, i.e. all of the following expressions are `true`.
35+
</p>
36+
<ul>
37+
<li><p><tt>noexcept(rcvr)</tt>,</p></li>
38+
<li><p><tt>(noexcept(vs) &amp;&amp; ...)</tt>,</p></li>
39+
<li><p>the member-function call to `rcvr.set_value(vs...)` including any implicit conversions of arguments.</p></li>
40+
</ul>
41+
<p>
42+
This means that if, for example, one of the sub-expressions in the pack `vs` was a call to some potentially-throwing
43+
function then the overall `set_value` expression would be violating the mandates requirement.
44+
<p/>
45+
For example:
46+
</p>
47+
<blockquote><pre>
48+
struct my_receiver
49+
{
50+
void set_value(int x) noexcept;
51+
};
52+
53+
int get_value() noexcept(false);
54+
55+
my_receiver r;
56+
std::execution::set_value(r, get_value()); // <span style="color:#C80000;font-weight:bold">fails MANDATE-NOTHROW mandates</span>
57+
</pre></blockquote>
58+
<p>
59+
Instead, we need to redefine these CPOs as being expression-equivalent to something that does not require that the
60+
argument expressions to the CPO themselves are `noexcept` &mdash; only what will be in the body of the CPO function.
61+
<p/>
62+
For example, we could change <sref ref="[exec.set.value]"/> to define `set_value(rcvr, vs...)` as expression-equivalent to:
63+
</p>
64+
<blockquote><pre>
65+
[](auto&amp;&amp; rcvr2, auto&amp;&amp;... vs2) noexcept -&gt;
66+
decltype(auto) requires requires { std::forward&lt;decltype(rcvr2)&gt;(rcvr2).set_value(std::forward&lt;decltype(vs2)&gt;(vs2)...); }
67+
{
68+
return <i>MANDATE-NOTHROW</i>(std::forward&lt;decltype(rcvr2)&gt;(rcvr2).set_value(std::forward&lt;decltype(vs2)&gt;(vs2)...));
69+
}(rcvr, vs...)
70+
</pre></blockquote>
71+
<p>
72+
The following sections all contain problematic uses of <tt><i>MANDATE-NOTHROW</i></tt>:
73+
</p>
74+
<ul>
75+
<li><p><sref ref="[exec.get.allocator]"/></p></li>
76+
<li><p><sref ref="[exec.get.stop.token]"/></p></li>
77+
<li><p><sref ref="[exec.get.env]"/></p></li>
78+
<li><p><sref ref="[exec.get.domain]"/></p></li>
79+
<li><p><sref ref="[exec.get.scheduler]"/></p></li>
80+
<li><p><sref ref="[exec.get.delegation.scheduler]"/></p></li>
81+
<li><p><sref ref="[exec.get.fwd.progress]"/></p></li>
82+
<li><p><sref ref="[exec.get.compl.sched]"/></p></li>
83+
<li><p><sref ref="[exec.get.await.adapt]"/></p></li>
84+
<li><p><sref ref="[exec.set.value]"/></p></li>
85+
<li><p><sref ref="[exec.set.error]"/></p></li>
86+
<li><p><sref ref="[exec.set.stopped]"/></p></li>
87+
<li><p><sref ref="[exec.opstate.start]"/></p></li>
88+
</ul>
89+
</discussion>
90+
91+
<resolution>
92+
<p>
93+
</p>
94+
</resolution>
95+
96+
</issue>

0 commit comments

Comments
 (0)