> On 27 Aug 2024, at 21:21, Thiago Macieira <[email protected]> wrote:
> 
> On Tuesday 27 August 2024 10:29:05 GMT-7 Ville Voutilainen wrote:
>> On Tue, 27 Aug 2024 at 17:15, Thiago Macieira <[email protected]>
> wrote:
>>> The point is that this putative mistake has no consequences, today. It
>>> remains to be seen whether it will with contracts, when those come. When
>>> contracts come, if those noexcept are a problem, then both libc++ and
>>> libstdc++ will deploy a solution, which should suffice for us too. Until
>>> then, I don't see a reason to deprive ourselves of any potential benefits
>>> that the noexcept might bring.
>> 
>> What benefits?
> 
> TBH, it's actually very little. But little is not zero.

TL;DR: Given that the tradeoff is between two marginally and somewhat 
speculative benefits, it seems to me that a pragmatic (as opposed to dogmatic) 
application of the Lakos rule in the cases where Q_ASSERT is used to verify 
preconditions is sensible for us. Which in our governance model translates to 
maintainer privilege. In other words: if Thiago believes that making something 
noexcept is correct because a try/catch on the call site would be the 
completely wrong tool to deal with the mistake that the Q_ASSERT guards 
against, then that’s acceptable (and doesn’t mean that it cannot be discussed 
passionately in code review).


We seem to have a tradeoff between two minor benefits: on the one hand, better 
performance and binary size by marking functions that assert preconditions as 
noexcept. On the other hand, the somewhat speculative hope that, by making such 
noexpect(false), we can in some future benefit from improved testability, 
support for different error handling paradigms coming from maybe-C++26 
contracts, or otherwise. I’d also throw in that by making a function noexcept, 
we tell users of the API that they don’t have to bother with a try/catch block 
around that particular call, and that try/catch in those cases are the wrong 
tool to guard against “stupid” errors, whatever those are (perhaps: errors that 
could be prevented by static analysis).

Since Lakos is referenced in this thread, two quotes from the paper [1] that 
resulted in P3155R0 [2], aka “The Lakos Rule”:

On page 5 of [1]:

> What Are We Proposing
> In order to support effective testing, compilers should offer two ‘modes’ for 
> handling noexcept violations.
> 
> i/ a “production” mode, the default, which guarantees to terminate the 
> process rather than allow an exception to propagate past a noexcept exception 
> specification.
> 
> ii/ a “testing” mode, which performs regular stack unwinding if an exception 
> propagates beyond a noexcept exception specification. This would imply 
> disabling any optimizations based on static analysis of exception 
> specifications, while ensuring that the noexcept operator continues to see 
> the same result.

On page 8 of [1]:

> Preferred additional recommendation
> If the core language can be amended to support a testing mode, we recommend 
> the following guideline:
> Each operation whose behavior is such that it clearly cannot throw, when 
> called with arguments satisfying function preconditions (and its own object 
> state invariants), should be marked as unconditionally noexcept.

The core language, as of today, has not been amended to support a “testing 
mode” (where, as per proposal, an exception propagating past a noexcept 
function would not terminate). However, Q_ASSERT as a macro that expands to 
nothing in a “production” mode build, seems to me to be very similar in spirit: 
we don’t assert in a release build, so we can guarantee that we don’t throw an 
exception in code like the referenced

       static Qt::strong_ordering compareThreeWay_helper(const Iterator &lhs,
                                                         const Iterator &rhs) 
noexcept
       {
           Q_ASSERT(lhs.item.d == rhs.item.d);
           return Qt::compareThreeWay(lhs.item.i, rhs.item.i);
       }

This function, when called with arguments satisfying function preconditions, 
cannot throw. In the “test” mode, aka “debug build”, it does whatever we want 
Q_ASSERT to do.

Making it possible for us to test the effectiveness of asserts, esp when they 
are used to implement a “contract”, is a good goal. Throwing an exception would 
be one way to do that, but not the only one. Fork’ing, or using an assert 
handler, or defining Q_ASSERT to spit out a qCritical that makes the test fail 
(via QTest::failOnWarning), are all possible ways to do that today already.

One can argue that comparing iterators from two different lists resulting in 
undefined behavior is not an unexpected issue; the behavior of comparing two 
iterators over different ranges is undefined [3] (unless the respective 
comparison operator has an explicit noexcept specification). Does the same 
logic apply for calling std::vector::operator[] with an index out of bounds? Is 
it material to the principle that the index can come from somewhere else, and 
that the vector is probably long-lived and might have changed after the 
boundaries were asserted? It is well-defined that calling std::optional::value 
on a nullopt throws an exception, while it is not defined what operator[] does 
(but it will be, once a C++26 contract is added to it).

So, somewhat marginal technical benefits aside, what “noexcept” in a library 
API communicates to the caller of the API is whether or not they should 
consider exception handling as a tool to make their code more resilient. We do 
need to make sure that users can handle problems gracefully so that their 
program becomes resilient against unexpected issues. We already make it close 
to impossible to use C++ exception handling to that end (ref recent thread [4]).


Volker

[1] https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3248.pdf
[2] https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3155r0.pdf
[3] https://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#446 - open and 
pending because other things are evidently more important :)
[4] https://lists.qt-project.org/pipermail/development/2024-May/045252.html

-- 
Development mailing list
[email protected]
https://lists.qt-project.org/listinfo/development

Reply via email to