|
| 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 — 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) && ...)</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` — 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&& rcvr2, auto&&... vs2) noexcept -> |
| 66 | + decltype(auto) requires requires { std::forward<decltype(rcvr2)>(rcvr2).set_value(std::forward<decltype(vs2)>(vs2)...); } |
| 67 | +{ |
| 68 | + return <i>MANDATE-NOTHROW</i>(std::forward<decltype(rcvr2)>(rcvr2).set_value(std::forward<decltype(vs2)>(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