Date: Sun, 27 Apr 2025 10:44:42 -0400
On Sun, Apr 27, 2025 at 8:20 AM Jonathan Wakely via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> On Sun, 27 Apr 2025, 00:17 Frederick Virchanza Gotham via Std-Proposals, <
> std-proposals_at_[hidden]> wrote:
>
>> The whole 'noexcept' thing has gotten a bit hairy in C++. We've
>> already thrown 'throw' specifiers out the window, but even now in
>> C++26 the situation isn't ideal, given that people are now writing
>> code as follows:
>>
>> template<typename T>
>> bool Func(T &arg) noexcept ( noexcept(++arg) && noexcept((bool)!!arg)
>> )
>> {
>> ++arg;
>> return !!arg;
>> }
>>
>> . . .which is why some people are now asking for:
>>
>> template<typename T>
>> bool Func(T &arg) noexcept(auto)
>> {
>> ++arg;
>> return !!arg;
>> }
>> [...]
>> I think we should have a way of marking a function so that the
>> compiler will refuse to compile it if it:
>> (1) throws an exception (e.g. by using 'throw' or
>> 'std::rethrow_exception')
>> or
>> (2) calls another function that isn't marked as 'noexcept'
>>
>> I think this would also allow the compiler to optimise more aggressively.
>>
>
> How? The compiler can already see whether the function calls anything
> which can throw. It seems to me that with this suggested feature, every
> function is either compiled identically to how it's compiled today, or it
> becomes ill-formed and doesn't compile at all.
>
> When would this help optimisation?
>
It wouldn't.
But it would help with the problem I call "rogue terminates" — where you
write a noexcept-spec but you get it just slightly too strict, so that the
compiler's implicitly generated "invisible exception handler" is *not* a
no-op, and actually ends up calling `std::terminate` in a corner case.
I wrote about this in 2018:
https://quuxplusone.github.io/blog/2018/06/12/attribute-noexcept-verify/
The example of a "rogue terminate" in my blog post is:
template<class T>
> auto frobnicate(T t)
> noexcept(std::is_nothrow_copy_constructible_v<T>)
> {
> return t;
> }
>
static_assert(noexcept(frobnicate(42)));
> static_assert(not noexcept(frobnicate(std::string{})));
I concluded:
The problem with noexcept(auto), outlined above, boils down to:
> noexcept(auto) instructs the compiler to take the wheel. For this to
> work, the compiler has to be a dang good driver — it has to guess correctly
> for each function whether we intended it to be noexcept or not. As we saw
> in the first example, humans are *terrible* at this game — but as we saw
> in the second example, compilers seem *also* to be terrible at it. We
> can’t hand the wheel to the compiler unless we know the compiler is going
> to drive right.
> So, trust, but verify <https://en.wikipedia.org/wiki/Trust,_but_verify>?
> Suppose we had a vendor-specific attribute — let’s for the sake of argument
> call it [[clang::noexcept_verify]] — which would instruct the compiler to
> compute the same exact thing as noexcept(auto), but then, *instead of
> applying it blindly to our function’s signature*, the compiler would
> merely look for an existing (possibly defaulted) noexcept specification
> on our function and *verify* that the computed noexcept-ness matched the
> noexcept-ness expressed by our existing specification!
> The compiled program would still use the noexcept-ness expressed by the
> programmer. The computed noexcept-ness would be used *only* for
> diagnostics; and the diagnostics would not necessarily have to be fatal.
> (They could be dialed down to warnings.) This would turn our “rogue
> std::terminate” example above into a compile-time failure.
Nobody ever took me up on the idea of implementing it in a compiler,
though, AFAIK.
(I never pursued it myself.)
–Arthur
std-proposals_at_[hidden]> wrote:
> On Sun, 27 Apr 2025, 00:17 Frederick Virchanza Gotham via Std-Proposals, <
> std-proposals_at_[hidden]> wrote:
>
>> The whole 'noexcept' thing has gotten a bit hairy in C++. We've
>> already thrown 'throw' specifiers out the window, but even now in
>> C++26 the situation isn't ideal, given that people are now writing
>> code as follows:
>>
>> template<typename T>
>> bool Func(T &arg) noexcept ( noexcept(++arg) && noexcept((bool)!!arg)
>> )
>> {
>> ++arg;
>> return !!arg;
>> }
>>
>> . . .which is why some people are now asking for:
>>
>> template<typename T>
>> bool Func(T &arg) noexcept(auto)
>> {
>> ++arg;
>> return !!arg;
>> }
>> [...]
>> I think we should have a way of marking a function so that the
>> compiler will refuse to compile it if it:
>> (1) throws an exception (e.g. by using 'throw' or
>> 'std::rethrow_exception')
>> or
>> (2) calls another function that isn't marked as 'noexcept'
>>
>> I think this would also allow the compiler to optimise more aggressively.
>>
>
> How? The compiler can already see whether the function calls anything
> which can throw. It seems to me that with this suggested feature, every
> function is either compiled identically to how it's compiled today, or it
> becomes ill-formed and doesn't compile at all.
>
> When would this help optimisation?
>
It wouldn't.
But it would help with the problem I call "rogue terminates" — where you
write a noexcept-spec but you get it just slightly too strict, so that the
compiler's implicitly generated "invisible exception handler" is *not* a
no-op, and actually ends up calling `std::terminate` in a corner case.
I wrote about this in 2018:
https://quuxplusone.github.io/blog/2018/06/12/attribute-noexcept-verify/
The example of a "rogue terminate" in my blog post is:
template<class T>
> auto frobnicate(T t)
> noexcept(std::is_nothrow_copy_constructible_v<T>)
> {
> return t;
> }
>
static_assert(noexcept(frobnicate(42)));
> static_assert(not noexcept(frobnicate(std::string{})));
I concluded:
The problem with noexcept(auto), outlined above, boils down to:
> noexcept(auto) instructs the compiler to take the wheel. For this to
> work, the compiler has to be a dang good driver — it has to guess correctly
> for each function whether we intended it to be noexcept or not. As we saw
> in the first example, humans are *terrible* at this game — but as we saw
> in the second example, compilers seem *also* to be terrible at it. We
> can’t hand the wheel to the compiler unless we know the compiler is going
> to drive right.
> So, trust, but verify <https://en.wikipedia.org/wiki/Trust,_but_verify>?
> Suppose we had a vendor-specific attribute — let’s for the sake of argument
> call it [[clang::noexcept_verify]] — which would instruct the compiler to
> compute the same exact thing as noexcept(auto), but then, *instead of
> applying it blindly to our function’s signature*, the compiler would
> merely look for an existing (possibly defaulted) noexcept specification
> on our function and *verify* that the computed noexcept-ness matched the
> noexcept-ness expressed by our existing specification!
> The compiled program would still use the noexcept-ness expressed by the
> programmer. The computed noexcept-ness would be used *only* for
> diagnostics; and the diagnostics would not necessarily have to be fatal.
> (They could be dialed down to warnings.) This would turn our “rogue
> std::terminate” example above into a compile-time failure.
Nobody ever took me up on the idea of implementing it in a compiler,
though, AFAIK.
(I never pursued it myself.)
–Arthur
Received on 2025-04-27 14:44:56