[Development] qsizetype
Marc Mutz
marc.mutz at qt.io
Thu Sep 1 12:15:32 CEST 2022
Hi,
You may have seen the qsizetype patches we've been pushing over the last
months. This is to give my perspective on the issue and why I think it's
needed:
First off, while I made QStringView qsizetype'd, because it was designed
to take input from STL containers[1], I was not involved in the decision
to port even just the Qt containers to qsizetype in 6.0.
[1] (it originally used size_t, which is what the STL containers use as
size_type but it was requested to use a signed type during review)
I do observe, though, that, starting with the premiss of a Qt container
of size > 2 Gi, the current code base has a pretty high bug density in
this area:
- https://codereview.qt-project.org/c/qt/qtbase/+/422246
- https://codereview.qt-project.org/c/qt/qtbase/+/422989
- https://codereview.qt-project.org/c/qt/qtbase/+/421912
- https://codereview.qt-project.org/c/qt/qtbase/+/422999
- https://codereview.qt-project.org/c/qt/qtbase/+/403614
- https://codereview.qt-project.org/c/qt/qtbase/+/358114
The goal of the exercise was initially twofold: 1. to fix bugs, assuming
the Qt containers hold more than 2 Gi of elements and 2. go back to a CI
build where narrowing conversions cause compile errors. I believe we had
that in Qt 5, at least on MSVC, and it was apparently disabled for 6.0.
The majority of changes are targeting the second goal. Bugs that fall in
the first category, like the ones listed above. are user-visible,
usually get a commit and Jira ticket of their own.
I don't really want to start a discussion on whether (owning) Qt
containers _should_ support more than 2 Gi elements in the first place.
>From my pov, that decision has been made in the run-up to 6.0, and
everyone who knows me knows that I don't much care about the owning Qt
containers and their shortcomings.
I just want to raise awareness of the issue, which, really, is just an
incomplete port of the Qt containers to qsizetype in 6.0.
Over the last months, however, I've realized that "just the Qt
containers" introduces a nasty API inconsistency in Qt: qsizetype in Qt
containers and int elsewhere. This inconsistency is un-Qt-ish and
creates problems for our users.
First up, it's unclear what a Qt container is: is QRegion a container?
What does numRects() return when you setRects with a QList with > 2Gi
elements? Where do we draw the line between Qt container and Qt
non-container? See, in particular, QDataStream's inability to serialize
containers > 2 Gi (and, in the case of QString, even > 1 Gi).
Second, the truncation that users face on c.size() (qsizetype) ->
setFoo() (int) is using modulo arithmetic, not saturation arithmetic, so
the result is very unpredictable: INT_MAX + 1 elements turn into 0
elements, not INT_MAX, etc, creating a lot of opportunities for False
Positives or Negatives, even with modestly oversized containers (INT_MAX
+ ε). The central Qt API guideline is to make APIs easy to use and hard
to misuse. Both parts are violated here, especially since, while
compilers regularly warn about signed/unsigned mismatches, they seldomly
warn about narrowing (proof: Qt compiles). To fix the inconsistency
means to port also the "non-container" APIs to qsizetype, and
handle/detect/defend against overflow _centrally_, so we don't inflict
the issue on every user of the APIs.
This should be non-issue for function parameters, because widening isn't
an error. The value is less pronounced for return values, because
widening those may cause narrowing in user code, but the hope is that
once we've progressed somewhat, we can enlist compiler support by
globally enabling warnings such as -Wshorten-64-to-32, even though, as
the porting guide included in the
https://bugreports.qt.io/browse/QTBUG-102461 epic describes, this will
not catch manual casts, which is why, sadly, one needs to look at every
int/uint manually.
Comments (and contributions) welcome.
Thanks,
Marc
More information about the Development
mailing list