[Development] Disavowing the Lakos Rule for Q_ASSERT

Marc Mutz marc.mutz at qt.io
Tue Aug 27 07:53:18 CEST 2024


On 26.08.24 22:12, Thiago Macieira wrote:
> On Monday 26 August 2024 12:55:04 GMT-7 Marc Mutz via Development wrote:
>> I don't see a proposal for an alternative rule in the above. Please
>> provide a) a new rule and b) rationale for it. Bonus points for being
>> implementable in a static checker like Clazy or Axivion.
> 
> "Q_ASSERT don't affect noexceptness"
> 
> Or
> 
> "noexcept(false) if you call other, noexcept(false) functions from your code",
> which includes all the pthread cancellation points in glibc. Since qt_assert
> is noexcept, Q_ASSERT is not included. This is very easy to implement with a
> static checker.
> 
> We could be more complex, with "Q_ASSERT that check preconditions imply
> noexcept(false) but Q_ASSERT that verify the state of the internal invariant
> against corruption don't". This would not be easy to implement with a static
> checker.

If we make the distinction between Q_ASSERT-as-precondition-check and 
Q_ASSERT-as-internal-invariant/state-check (with a new macro 
name/names), then I can agree to that. Note, however, that the example 
that sparked this discussion (View::slice(int, int)) remains 
noexcept(false) under this rule, too. Ditto op==(It, It).

I have tried to adjust 
https://wiki.qt.io/Things_To_Look_Out_For_In_Reviews#noexcept 
accordingly. For offline reader convenience, it currently reads:

>     Only functions that cannot fail to meet their post-conditions can be noexcept. Failure to meet post-conditions can stem from a variety of reasons, including a) having preconditions that are not met, b) calling a Posix Cancellation Point or another noexcept(false) function (which may prematurely end the function), or c) allocating memory or other resources (file handles, mutexes). Rationale: This is the Lakos Rule (latest version: https://wg21.link/p2861). We should track what the standard does and not invent rules of our own. Specifically for preconditions, there are vague plans to make them testable from QtTest by making Q_ASSERT() throw an exception instead of calling abort() on failure; any noexcept function in-between will cause this to not work. Specifically for (b), some Posix systems implement thread cancellation by throwing an exception-like object to unwind the stack (e.g. glibc-based ones). Such exceptions must not be swallowed by user code (incl. Qt code) lest thread cancellations ends in std::terminate().
>         Exception: Like the std smart pointers, our smart pointers operator*() may also be noexcept, but then they must not contain a Q_ASSERT(). Rationale: When in Rome^WC++, do as the Romans do^W^Wstd does.
>         Distinguish Q_ASSERT()s that check for precondition violations (= user is responsible) and those that check internal state/invariants in our code (= we are responsible). The former requires the function containing them to be noexcept(false), the latter doesn't. Rationale: The Lakos Rule is about contract violations, not about internal consistency checks. If internal consistency is found to be in disarray, recovery is likely impossible. If a call contract is violated, OTOH, that may be very much recoverable, because it's detected before "something bad" may happen. We'll hopefully get separate macros for the two, but for now, be aware of the two different use cases of assertions.
> 


Thanks,
Marc

-- 
Marc Mutz <marc.mutz at qt.io> (he/his)
Principal Software Engineer

The Qt Company
Erich-Thilo-Str. 10 12489
Berlin, Germany
www.qt.io

Geschäftsführer: Mika Pälsi, Juha Varelius, Jouni Lintunen
Sitz der Gesellschaft: Berlin,
Registergericht: Amtsgericht Charlottenburg,
HRB 144331 B



More information about the Development mailing list