[Development] Disavowing the Lakos Rule for Q_ASSERT
Thiago Macieira
thiago.macieira at intel.com
Thu Aug 29 17:31:54 CEST 2024
On Thursday 29 August 2024 06:43:58 GMT-7 Marc Mutz via Development wrote:
> I'm ok with 1-3, I'm not ok with 4.
>
> The state of the art is what the std prescribes, not what a stdlib
> implementation does. stdlibs are magic; they can theoretically remove
> the noexcept for new code and keep it for old. Or they can let contract
> violations pass magically. We can't, at least not with a lot of manual
> labour (QT_REMOVED_SINCE/QT6_NEW_OVERLOAD).
They're not _that_ magic. There is a certain amount of compiler-helped magic
in them, true, but the majority of the Standard Libraries are simply libraries
written in standard C++. So whatever solution they come up with, even if not
part of the standard, may be something we can use too. If they decide for a
binary- or source-incompatible build mode, we can too. If they decide for some
attribute marker, we can too.
Moreover, this itself should be a consideration for the contracts feature in
the first place, both for standardisation and for implementations. That is, the
proponents of the feature ought to provide a means for adoption in content
that is currently noexcept (wide or mostly-wide contract).
> If we get (1), I think we'll very quickly also get a build mode in which
> these throw.
Unsupported mode. We don't use exceptions and we don't plan on beginning now.
You can make them throw, but you can't expect your application to work
properly afterwards because the *rest* of Qt isn't designed for it. For a
recent example, see https://codereview.qt-project.org/c/qt/qtbase/+/586032.
Specifically, see the message in PS1 where I noted that QProcess will be in an
unexpected and possibly corrupt state if an exception/unwind happens. Ossi had
me remove the text because it's un-actionable.
Which brings me to why I am asking this. We are deliberately tying our hands
for something that:
a) won't be in general use in Qt for some 6-8 years from today (thus may be
past 7.0)
b) needs adaptation in the Standard Library implementations and thus will
likely provide solutions to the same issue
c) requires exceptions, something we do not support
> And I'm also questioning the alternatives to throwing from assertions
> (or Q_PRE, if you want). You can fork, yes. Very complicated, and
> probably slow as hell (I still remember Qt 5 QSharedPointer negative
> tests were even slow on Linux). We can't call a QtTest-handler, because
> that handler cannot return without an exception, because the code
> following the assertion typically assumes that the assertion holds and
> runs into Language UB (non-recoverable) as a consequence if it didn't.
The negative tests weren't just forking. They were a fork-and-exec of the
compiler, then running the output. A fork-and-run-until-abort is much simpler
and should be very fast. But I agree it's not for every case, because there
are constraints in global state, such as open file descriptors (logs, sockets,
etc.) On the other hand, it may work in conditions where exceptions don't:
namely, exception-unsafe code.
Of which there's a lot in Qt.
PS: running stuff safely in fork-no-exec mode has been my $DAYJOB for the past
5 years. I have a lot of experience in this.
> Finally, one thing that Lakos wrote in his paper
> (https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2861r0.pdf)
> was that it should be up to *the owner of main()* to determine how
> precondition violations are handled. Neither std nor Qt should decide
> for the user here.
I agree with that in principle. The same rule applies to signal handlers like
SIGINT, SIGTERM and SIGSEGV: the owner of main() decides what action the
application should take. It may be a decision that they delegated to some
library for consistency with their ecosystem, but that itself is a decision.
However, Qt has explicitly disclaimed support for exceptions transiting
through its code. Therefore, the owner of main() may choose this mode, but
they need to be aware of where it may happen and how far the exception is
allowed to propagate. For one thing, C++ does not exist in isolation and the
stack trace can (and does) contain non-C++ frames that may be unable to
support unwinding. See all the issues with exceptions propagating through the
Qt event loop that used to work on Intel Macs but do not work on Apple ARM
silicon.
So, reality impinges and I question the ability to widely use exceptions for
contract violations.
> I think this is very pertinent to the current software security
> legislation being proposed on both sides and Qt should not shut the door
> in front of users that would like to run checked build modes in production.
>
> Therefore: no noexcept on narrow-contract functions is still paramount,
> IMNSHO.
I agree with the premise but not the conclusion. There are other ways of
verifying preconditions besides exceptions.
Tying this into my $DAYJOB: I work on finding the Silent Data Errors where the
processor (or any other compute unit) didn't execute the code as specified
producing data errors, and failed to detect it as such at that point. Ideally
we'd either correct the problem or not have it in the first place, but the next
best thing to that is identifying it as an uncorrectable error and halting
execution immediately. Forget exceptions; your process will get a SIGKILL in
the best case.
> Again, a hunch is not enough to sway me here. A meaningful increase in
> runtime speed would need to be demonstrated, and even then, we would
> need to weigh that feature against the desire for certain customers (one
> being ourselves, for negative testing) to use a checked build mode in
> production.
I'm willing to be pragmatic.
For the majority of inline code, the compiler won't care about the
noexceptness of the content: it *knows* whether something threw or not.
Examples are QStringView::sliced() or the QCborStreamReader members.
What I'd like to change is:
- for inline code, where the function's noexceptness is likely to be used in a
noexcept expression elsewhere and that causes slower code to be used
- for out-of-line code, where the precondition is unverifiable anyway (such as
"you've passed a valid pointer")
--
Thiago Macieira - thiago.macieira (AT) intel.com
Principal Engineer - Intel DCAI Platform & System Engineering
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 5152 bytes
Desc: not available
URL: <http://lists.qt-project.org/pipermail/development/attachments/20240829/e5faaea7/attachment-0001.bin>
More information about the Development
mailing list