diff options
author | Giuseppe D'Angelo <[email protected]> | 2025-02-02 22:17:42 +0100 |
---|---|---|
committer | Giuseppe D'Angelo <[email protected]> | 2025-02-28 23:40:58 +0100 |
commit | 1145e1709d1072f7dd45683e9c25a14615603854 (patch) | |
tree | 8bd7be93b147b54694f20e51103f42af5812dde0 | |
parent | 56de11397559af3b9694ef2b99d93a469889ae5e (diff) |
Geometry classes: introduce check for overflows
The integer-based geometry classes all risk triggering UB via signed
integer overflow. Their representation and APIs exclusively use `int`.
This means that for instance one can build a QLine spanning from
x1=INT_MIN to x2=INT_MAX, and they try asking its delta-x (dx()),
causing an integer overflow in the computation.
So far, this has always been "undefined behavior that works in
practice", effectively wrapping values around on all of our supported
implementation. This choice however makes it impossible to have hardened
/ ubsan builds of Qt. It also causes some bizarre behaviors: for
instance, a maximal QRect (going from (INT_MIN, INT_MIN) to (INT_MAX,
INT_MAX)) reports "false" when asked if it contains() a 10x10 rectangle.
(That's because QRect::contains() overflows.). Users may not readily
notice what is going wrong here.
After some thoughts, I've decided that the burden here lies on Qt.
While it's true that these geometry classes are nothing but glorified
int holders, their representation and handling is entirely chosen and
controlled by Qt. The user is not supposed to know "in advance" that
QRect holds a couple of points and not, say, a point and a size.
Qt also chose the shape of the APIs, taking and returning ints, even
when some of these APIs cannot return meaningful results due to the
chosen representation.
The best we can do at this point is to trigger assertions if some
calculation overflows. Therefore, deploy the newly introduced private
checked integer class, that will assert if something goes wrong. In
release builds they will keep the pre-existing behavior (wraparound),
but they will remove the associated UB.
The changes are mostly mechanical:
1) change the representation of the geometry classes to use the checked
integer (instead of plain `int`);
2) change getters/setters to deal with conversions from and to the
checked integer;
plus some minimal fallout. Operator overloading on the checked integer
keeps the code 99% similar, and doesn't decrease readability. Some care
is necessary to try and make all internal calculations use the checked
int, instead of falling back to operations on `int`.
I've tried to keep the behavior identical as much as possible (except of
course for detecting overflow). In particular I haven't reengineered the
existing implementations to so something "smarter" or use 64 bit
arithmetic if possible or so.
The only exception is the QRectF constructor from QRect. Since a QRectF
can faithfully represent any QRect, in the conversion I'm taking
special care not to call width() or height() as they may overflow.
Instead, I calculate them using int64. This allows for a maximal QRect
to be converted to a QRectF without loss of information (instead of what
was happening before).
Some other remarks:
* operator/ checks that the divisor is not zero. This was done in
several places already and just missing here and there.
* QRect::isNull had a bizarre behavior (autotested) where a maximal
rectangle was considered null (because of wraparounds). I've decided
to keep this behavior.
* Several test rows in tst_QRect were relying on overflow behaviors.
However, many others were already commented out, with a comment
saying "it would cause an overflow". Therefore I've commented them
out as well.
* The integral classes sometime have functions taking qreal (e.g.
`operator*`). In there, qRound is applied to keep the result integral.
In principle qRound can also overflow and cause UB, but I've decided
to tackle that separately because I'm not sure what should happen
(QTBUG-133261). This also happens for the fp->integral conversion
functions (e.g. QRectF::toRect()).
* The functions are marked constexpr, so add some minimal testing to
make sure I'm not breaking it.
* Some functions are also marked noexcept. We need to have a broader
discussion whether that's actually wrong, and how to recover from
there.
Task-number: QTBUG-98965
Task-number: QTBUG-132947
Task-number: QTBUG-133261
Change-Id: Idde1daa5c6ca3e37ba0e402617576c5d130615f5
Reviewed-by: Thiago Macieira <[email protected]>
-rw-r--r-- | src/corelib/tools/qline.h | 4 | ||||
-rw-r--r-- | src/corelib/tools/qmargins.h | 94 | ||||
-rw-r--r-- | src/corelib/tools/qpoint.h | 59 | ||||
-rw-r--r-- | src/corelib/tools/qrect.cpp | 68 | ||||
-rw-r--r-- | src/corelib/tools/qrect.h | 142 | ||||
-rw-r--r-- | src/corelib/tools/qsize.cpp | 12 | ||||
-rw-r--r-- | src/corelib/tools/qsize.h | 48 | ||||
-rw-r--r-- | src/gui/painting/qtransform.cpp | 8 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qline/tst_qline.cpp | 13 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qmargins/tst_qmargins.cpp | 14 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qpoint/tst_qpoint.cpp | 6 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qpointf/tst_qpointf.cpp | 6 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qrect/tst_qrect.cpp | 170 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qsize/tst_qsize.cpp | 6 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qsizef/tst_qsizef.cpp | 6 |
15 files changed, 406 insertions, 250 deletions
diff --git a/src/corelib/tools/qline.h b/src/corelib/tools/qline.h index 8970a084bbf..05d73f45b43 100644 --- a/src/corelib/tools/qline.h +++ b/src/corelib/tools/qline.h @@ -114,12 +114,12 @@ constexpr inline QPoint QLine::p2() const constexpr inline int QLine::dx() const { - return pt2.x() - pt1.x(); + return (pt2 - pt1).x(); } constexpr inline int QLine::dy() const { - return pt2.y() - pt1.y(); + return (pt2 - pt1).y(); } constexpr inline void QLine::translate(const QPoint &point) diff --git a/src/corelib/tools/qmargins.h b/src/corelib/tools/qmargins.h index 8782b147afc..fb3d70f64fe 100644 --- a/src/corelib/tools/qmargins.h +++ b/src/corelib/tools/qmargins.h @@ -4,6 +4,7 @@ #ifndef QMARGINS_H #define QMARGINS_H +#include <QtCore/qcheckedint_impl.h> #include <QtCore/qcompare.h> #include <QtCore/qnamespace.h> @@ -50,11 +51,34 @@ public: [[nodiscard]] constexpr inline QMarginsF toMarginsF() const noexcept; + friend constexpr inline QMargins operator+(const QMargins &m1, const QMargins &m2) noexcept; + friend constexpr inline QMargins operator-(const QMargins &m1, const QMargins &m2) noexcept; + friend constexpr inline QMargins operator+(const QMargins &lhs, int rhs) noexcept; + friend constexpr inline QMargins operator+(int lhs, const QMargins &rhs) noexcept; + friend constexpr inline QMargins operator-(const QMargins &lhs, int rhs) noexcept; + friend constexpr inline QMargins operator*(const QMargins &margins, int factor) noexcept; + friend constexpr inline QMargins operator*(int factor, const QMargins &margins) noexcept; + friend constexpr inline QMargins operator*(const QMargins &margins, qreal factor) noexcept; + friend constexpr inline QMargins operator*(qreal factor, const QMargins &margins) noexcept; + friend constexpr inline QMargins operator/(const QMargins &margins, int divisor); + friend constexpr inline QMargins operator/(const QMargins &margins, qreal divisor); + friend constexpr inline QMargins operator|(const QMargins &m1, const QMargins &m2) noexcept; + private: - int m_left; - int m_top; - int m_right; - int m_bottom; + using Representation = QtPrivate::QCheckedIntegers::QCheckedInt<int>; + + constexpr QMargins(Representation left, + Representation top, + Representation right, + Representation bottom) noexcept + : m_left(left), m_top(top), m_right(right), m_bottom(bottom) + { + } + + Representation m_left; + Representation m_top; + Representation m_right; + Representation m_bottom; friend constexpr bool comparesEqual(const QMargins &lhs, const QMargins &rhs) noexcept { @@ -72,13 +96,13 @@ private: friend constexpr decltype(auto) get(M &&m) noexcept { if constexpr (I == 0) - return q23::forward_like<M>(m.m_left); + return q23::forward_like<M>(m.m_left).as_underlying(); else if constexpr (I == 1) - return q23::forward_like<M>(m.m_top); + return q23::forward_like<M>(m.m_top).as_underlying(); else if constexpr (I == 2) - return q23::forward_like<M>(m.m_right); + return q23::forward_like<M>(m.m_right).as_underlying(); else if constexpr (I == 3) - return q23::forward_like<M>(m.m_bottom); + return q23::forward_like<M>(m.m_bottom).as_underlying(); } }; @@ -105,74 +129,75 @@ constexpr inline bool QMargins::isNull() const noexcept { return m_left==0 && m_top==0 && m_right==0 && m_bottom==0; } constexpr inline int QMargins::left() const noexcept -{ return m_left; } +{ return m_left.value(); } constexpr inline int QMargins::top() const noexcept -{ return m_top; } +{ return m_top.value(); } constexpr inline int QMargins::right() const noexcept -{ return m_right; } +{ return m_right.value(); } constexpr inline int QMargins::bottom() const noexcept -{ return m_bottom; } +{ return m_bottom.value(); } constexpr inline void QMargins::setLeft(int aleft) noexcept -{ m_left = aleft; } +{ m_left.setValue(aleft); } constexpr inline void QMargins::setTop(int atop) noexcept -{ m_top = atop; } +{ m_top.setValue(atop); } constexpr inline void QMargins::setRight(int aright) noexcept -{ m_right = aright; } +{ m_right.setValue(aright); } constexpr inline void QMargins::setBottom(int abottom) noexcept -{ m_bottom = abottom; } +{ m_bottom.setValue(abottom); } constexpr inline QMargins operator+(const QMargins &m1, const QMargins &m2) noexcept { - return QMargins(m1.left() + m2.left(), m1.top() + m2.top(), - m1.right() + m2.right(), m1.bottom() + m2.bottom()); + return QMargins(m1.m_left + m2.m_left, m1.m_top + m2.m_top, + m1.m_right + m2.m_right, m1.m_bottom + m2.m_bottom); } constexpr inline QMargins operator-(const QMargins &m1, const QMargins &m2) noexcept { - return QMargins(m1.left() - m2.left(), m1.top() - m2.top(), - m1.right() - m2.right(), m1.bottom() - m2.bottom()); + return QMargins(m1.m_left - m2.m_left, m1.m_top - m2.m_top, + m1.m_right - m2.m_right, m1.m_bottom - m2.m_bottom); } constexpr inline QMargins operator+(const QMargins &lhs, int rhs) noexcept { - return QMargins(lhs.left() + rhs, lhs.top() + rhs, - lhs.right() + rhs, lhs.bottom() + rhs); + return QMargins(lhs.m_left + rhs, lhs.m_top + rhs, + lhs.m_right + rhs, lhs.m_bottom + rhs); } constexpr inline QMargins operator+(int lhs, const QMargins &rhs) noexcept { - return QMargins(rhs.left() + lhs, rhs.top() + lhs, - rhs.right() + lhs, rhs.bottom() + lhs); + return QMargins(rhs.m_left + lhs, rhs.m_top + lhs, + rhs.m_right + lhs, rhs.m_bottom + lhs); } constexpr inline QMargins operator-(const QMargins &lhs, int rhs) noexcept { - return QMargins(lhs.left() - rhs, lhs.top() - rhs, - lhs.right() - rhs, lhs.bottom() - rhs); + return QMargins(lhs.m_left - rhs, lhs.m_top - rhs, + lhs.m_right - rhs, lhs.m_bottom - rhs); } constexpr inline QMargins operator*(const QMargins &margins, int factor) noexcept { - return QMargins(margins.left() * factor, margins.top() * factor, - margins.right() * factor, margins.bottom() * factor); + return QMargins(margins.m_left * factor, margins.m_top * factor, + margins.m_right * factor, margins.m_bottom * factor); } constexpr inline QMargins operator*(int factor, const QMargins &margins) noexcept { - return QMargins(margins.left() * factor, margins.top() * factor, - margins.right() * factor, margins.bottom() * factor); + return QMargins(margins.m_left * factor, margins.m_top * factor, + margins.m_right * factor, margins.m_bottom * factor); } constexpr inline QMargins operator*(const QMargins &margins, qreal factor) noexcept { + // Deliberately using left(), top() etc. (checked ints don't have FP arithmetic) return QMargins(qRound(margins.left() * factor), qRound(margins.top() * factor), qRound(margins.right() * factor), qRound(margins.bottom() * factor)); } @@ -185,20 +210,21 @@ constexpr inline QMargins operator*(qreal factor, const QMargins &margins) noexc constexpr inline QMargins operator/(const QMargins &margins, int divisor) { - return QMargins(margins.left() / divisor, margins.top() / divisor, - margins.right() / divisor, margins.bottom() / divisor); + return QMargins(margins.m_left / divisor, margins.m_top / divisor, + margins.m_right / divisor, margins.m_bottom / divisor); } constexpr inline QMargins operator/(const QMargins &margins, qreal divisor) { + Q_ASSERT(!qFuzzyIsNull(divisor)); return QMargins(qRound(margins.left() / divisor), qRound(margins.top() / divisor), qRound(margins.right() / divisor), qRound(margins.bottom() / divisor)); } constexpr inline QMargins operator|(const QMargins &m1, const QMargins &m2) noexcept { - return QMargins(qMax(m1.left(), m2.left()), qMax(m1.top(), m2.top()), - qMax(m1.right(), m2.right()), qMax(m1.bottom(), m2.bottom())); + return QMargins(qMax(m1.m_left, m2.m_left), qMax(m1.m_top, m2.m_top), + qMax(m1.m_right, m2.m_right), qMax(m1.m_bottom, m2.m_bottom)); } constexpr inline QMargins &QMargins::operator+=(const QMargins &margins) noexcept diff --git a/src/corelib/tools/qpoint.h b/src/corelib/tools/qpoint.h index d9a0fa53235..c652ef1daf8 100644 --- a/src/corelib/tools/qpoint.h +++ b/src/corelib/tools/qpoint.h @@ -4,6 +4,7 @@ #ifndef QPOINT_H #define QPOINT_H +#include <QtCore/qcheckedint_impl.h> #include <QtCore/qcompare.h> #include <QtCore/qnamespace.h> #include <QtCore/qnumeric.h> @@ -21,6 +22,7 @@ QT_ENABLE_P0846_SEMANTICS_FOR(get) class QDataStream; class QPointF; +class QRect; class QPoint { @@ -52,7 +54,7 @@ public: constexpr inline QPoint &operator/=(qreal divisor); constexpr static inline int dotProduct(const QPoint &p1, const QPoint &p2) - { return p1.xp * p2.xp + p1.yp * p2.yp; } + { return int(p1.xp * p2.xp + p1.yp * p2.yp); } private: friend constexpr bool comparesEqual(const QPoint &p1, const QPoint &p2) noexcept @@ -63,15 +65,15 @@ private: friend constexpr inline QPoint operator-(const QPoint &p1, const QPoint &p2) noexcept { return QPoint(p1.xp - p2.xp, p1.yp - p2.yp); } friend constexpr inline QPoint operator*(const QPoint &p, float factor) - { return QPoint(qRound(p.xp * factor), qRound(p.yp * factor)); } + { return QPoint(qRound(p.x() * factor), qRound(p.y() * factor)); } friend constexpr inline QPoint operator*(const QPoint &p, double factor) - { return QPoint(qRound(p.xp * factor), qRound(p.yp * factor)); } + { return QPoint(qRound(p.x() * factor), qRound(p.y() * factor)); } friend constexpr inline QPoint operator*(const QPoint &p, int factor) noexcept { return QPoint(p.xp * factor, p.yp * factor); } friend constexpr inline QPoint operator*(float factor, const QPoint &p) - { return QPoint(qRound(p.xp * factor), qRound(p.yp * factor)); } + { return QPoint(qRound(p.x() * factor), qRound(p.y() * factor)); } friend constexpr inline QPoint operator*(double factor, const QPoint &p) - { return QPoint(qRound(p.xp * factor), qRound(p.yp * factor)); } + { return QPoint(qRound(p.x() * factor), qRound(p.y() * factor)); } friend constexpr inline QPoint operator*(int factor, const QPoint &p) noexcept { return QPoint(p.xp * factor, p.yp * factor); } friend constexpr inline QPoint operator+(const QPoint &p) noexcept @@ -79,7 +81,10 @@ private: friend constexpr inline QPoint operator-(const QPoint &p) noexcept { return QPoint(-p.xp, -p.yp); } friend constexpr inline QPoint operator/(const QPoint &p, qreal c) - { return QPoint(qRound(p.xp / c), qRound(p.yp / c)); } + { + Q_ASSERT(!qFuzzyIsNull(c)); + return QPoint(qRound(p.x() / c), qRound(p.y() / c)); + } public: #if defined(Q_OS_DARWIN) || defined(Q_QDOC) @@ -88,9 +93,14 @@ public: [[nodiscard]] constexpr inline QPointF toPointF() const noexcept; private: - friend class QTransform; - int xp; - int yp; + using Representation = QtPrivate::QCheckedIntegers::QCheckedInt<int>; + + friend class QRect; + constexpr QPoint(Representation xpos, Representation ypos) noexcept + : xp(xpos), yp(ypos) {} + + Representation xp; + Representation yp; template <std::size_t I, typename P, @@ -99,9 +109,9 @@ private: friend constexpr decltype(auto) get(P &&p) noexcept { if constexpr (I == 0) - return q23::forward_like<P>(p.xp); + return q23::forward_like<P>(p.xp).as_underlying(); else if constexpr (I == 1) - return q23::forward_like<P>(p.yp); + return q23::forward_like<P>(p.yp).as_underlying(); } }; @@ -130,37 +140,37 @@ constexpr inline bool QPoint::isNull() const noexcept constexpr inline int QPoint::x() const noexcept { - return xp; + return xp.value(); } constexpr inline int QPoint::y() const noexcept { - return yp; + return yp.value(); } constexpr inline void QPoint::setX(int xpos) noexcept { - xp = xpos; + xp.setValue(xpos); } constexpr inline void QPoint::setY(int ypos) noexcept { - yp = ypos; + yp.setValue(ypos); } inline int constexpr QPoint::manhattanLength() const { - return qAbs(x()) + qAbs(y()); + return (qAbs(xp) + qAbs(yp)).value(); } constexpr inline int &QPoint::rx() noexcept { - return xp; + return xp.as_underlying(); } constexpr inline int &QPoint::ry() noexcept { - return yp; + return yp.as_underlying(); } constexpr inline QPoint &QPoint::operator+=(const QPoint &p) @@ -179,15 +189,15 @@ constexpr inline QPoint &QPoint::operator-=(const QPoint &p) constexpr inline QPoint &QPoint::operator*=(float factor) { - xp = qRound(xp * factor); - yp = qRound(yp * factor); + xp.setValue(qRound(x() * factor)); + yp.setValue(qRound(y() * factor)); return *this; } constexpr inline QPoint &QPoint::operator*=(double factor) { - xp = qRound(xp * factor); - yp = qRound(yp * factor); + xp.setValue(qRound(x() * factor)); + yp.setValue(qRound(y() * factor)); return *this; } @@ -200,8 +210,9 @@ constexpr inline QPoint &QPoint::operator*=(int factor) constexpr inline QPoint &QPoint::operator/=(qreal c) { - xp = qRound(xp / c); - yp = qRound(yp / c); + Q_ASSERT(!qFuzzyIsNull(c)); + xp.setValue(qRound(int(xp) / c)); + yp.setValue(qRound(int(yp) / c)); return *this; } diff --git a/src/corelib/tools/qrect.cpp b/src/corelib/tools/qrect.cpp index a82183bc804..6ca3caa0f44 100644 --- a/src/corelib/tools/qrect.cpp +++ b/src/corelib/tools/qrect.cpp @@ -788,7 +788,7 @@ QRect QRect::normalized() const noexcept bool QRect::contains(const QPoint &p, bool proper) const noexcept { - int l, r; + Representation l, r; if (x2 < x1 - 1) { l = x2 + 1; r = x1 - 1; @@ -803,7 +803,7 @@ bool QRect::contains(const QPoint &p, bool proper) const noexcept if (p.x() < l || p.x() > r) return false; } - int t, b; + Representation t, b; if (y2 < y1 - 1) { t = y2 + 1; b = y1 - 1; @@ -855,15 +855,15 @@ bool QRect::contains(const QRect &r, bool proper) const noexcept if (isNull() || r.isNull()) return false; - int l1 = x1; - int r1 = x1 - 1; + Representation l1 = x1; + Representation r1 = x1 - 1; if (x2 < x1 - 1) l1 = x2 + 1; else r1 = x2; - int l2 = r.x1; - int r2 = r.x1 - 1; + Representation l2 = r.x1; + Representation r2 = r.x1 - 1; if (r.x2 < r.x1 - 1) l2 = r.x2 + 1; else @@ -877,15 +877,15 @@ bool QRect::contains(const QRect &r, bool proper) const noexcept return false; } - int t1 = y1; - int b1 = y1 - 1; + Representation t1 = y1; + Representation b1 = y1 - 1; if (y2 < y1 - 1) t1 = y2 + 1; else b1 = y2; - int t2 = r.y1; - int b2 = r.y1 - 1; + Representation t2 = r.y1; + Representation b2 = r.y1 - 1; if (r.y2 < r.y1 - 1) t2 = r.y2 + 1; else @@ -935,29 +935,29 @@ QRect QRect::operator|(const QRect &r) const noexcept if (r.isNull()) return *this; - int l1 = x1; - int r1 = x1 - 1; + Representation l1 = x1; + Representation r1 = x1 - 1; if (x2 < x1 - 1) l1 = x2 + 1; else r1 = x2; - int l2 = r.x1; - int r2 = r.x1 - 1; + Representation l2 = r.x1; + Representation r2 = r.x1 - 1; if (r.x2 < r.x1 - 1) l2 = r.x2 + 1; else r2 = r.x2; - int t1 = y1; - int b1 = y1 - 1; + Representation t1 = y1; + Representation b1 = y1 - 1; if (y2 < y1 - 1) t1 = y2 + 1; else b1 = y2; - int t2 = r.y1; - int b2 = r.y1 - 1; + Representation t2 = r.y1; + Representation b2 = r.y1 - 1; if (r.y2 < r.y1 - 1) t2 = r.y2 + 1; else @@ -997,15 +997,15 @@ QRect QRect::operator&(const QRect &r) const noexcept if (isNull() || r.isNull()) return QRect(); - int l1 = x1; - int r1 = x2; + Representation l1 = x1; + Representation r1 = x2; if (x2 < x1 - 1) { l1 = x2 + 1; r1 = x1 - 1; } - int l2 = r.x1; - int r2 = r.x2; + Representation l2 = r.x1; + Representation r2 = r.x2; if (r.x2 < r.x1 - 1) { l2 = r.x2 + 1; r2 = r.x1 - 1; @@ -1014,15 +1014,15 @@ QRect QRect::operator&(const QRect &r) const noexcept if (l1 > r2 || l2 > r1) return QRect(); - int t1 = y1; - int b1 = y2; + Representation t1 = y1; + Representation b1 = y2; if (y2 < y1 - 1) { t1 = y2 + 1; b1 = y1 - 1; } - int t2 = r.y1; - int b2 = r.y2; + Representation t2 = r.y1; + Representation b2 = r.y2; if (r.y2 < r.y1 - 1) { t2 = r.y2 + 1; b2 = r.y1 - 1; @@ -1069,15 +1069,15 @@ bool QRect::intersects(const QRect &r) const noexcept if (isNull() || r.isNull()) return false; - int l1 = x1; - int r1 = x2; + Representation l1 = x1; + Representation r1 = x2; if (x2 < x1 - 1) { l1 = x2 + 1; r1 = x1 - 1; } - int l2 = r.x1; - int r2 = r.x2; + Representation l2 = r.x1; + Representation r2 = r.x2; if (r.x2 < r.x1 - 1) { l2 = r.x2 + 1; r2 = r.x1 - 1; @@ -1086,15 +1086,15 @@ bool QRect::intersects(const QRect &r) const noexcept if (l1 > r2 || l2 > r1) return false; - int t1 = y1; - int b1 = y2; + Representation t1 = y1; + Representation b1 = y2; if (y2 < y1 - 1) { t1 = y2 + 1; b1 = y1 - 1; } - int t2 = r.y1; - int b2 = r.y2; + Representation t2 = r.y1; + Representation b2 = r.y2; if (r.y2 < r.y1 - 1) { t2 = r.y2 + 1; b2 = r.y1 - 1; diff --git a/src/corelib/tools/qrect.h b/src/corelib/tools/qrect.h index fb938b00560..04f3b9a6d67 100644 --- a/src/corelib/tools/qrect.h +++ b/src/corelib/tools/qrect.h @@ -4,6 +4,7 @@ #ifndef QRECT_H #define QRECT_H +#include <QtCore/qcheckedint_impl.h> #include <QtCore/qhashfunctions.h> #include <QtCore/qmargins.h> #include <QtCore/qsize.h> @@ -132,10 +133,20 @@ public: [[nodiscard]] constexpr inline QRectF toRectF() const noexcept; private: - int x1; - int y1; - int x2; - int y2; + using Representation = QtPrivate::QCheckedIntegers::QCheckedInt<int>; + + constexpr QRect(Representation aleft, + Representation atop, + Representation awidth, + Representation aheight) noexcept + : x1(aleft), y1(atop), + x2(aleft + awidth - 1), y2(atop + aheight - 1) + {} + + Representation x1; + Representation y1; + Representation x2; + Representation y2; }; Q_DECLARE_TYPEINFO(QRect, Q_RELOCATABLE_TYPE); @@ -153,16 +164,24 @@ Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QRect &); *****************************************************************************/ constexpr inline QRect::QRect(int aleft, int atop, int awidth, int aheight) noexcept - : x1(aleft), y1(atop), x2(aleft + awidth - 1), y2(atop + aheight - 1) {} + : QRect(Representation(aleft), Representation(atop), Representation(awidth), Representation(aheight)) {} constexpr inline QRect::QRect(const QPoint &atopLeft, const QPoint &abottomRight) noexcept : x1(atopLeft.x()), y1(atopLeft.y()), x2(abottomRight.x()), y2(abottomRight.y()) {} constexpr inline QRect::QRect(const QPoint &atopLeft, const QSize &asize) noexcept - : x1(atopLeft.x()), y1(atopLeft.y()), x2(atopLeft.x()+asize.width() - 1), y2(atopLeft.y()+asize.height() - 1) {} + : x1(atopLeft.x()), y1(atopLeft.y()), x2(x1 + asize.width() - 1), y2(y1 + asize.height() - 1) {} constexpr inline bool QRect::isNull() const noexcept -{ return x2 == x1 - 1 && y2 == y1 - 1; } +{ + // This strange behavior is here for backwards compatibility -- a + // rectangle spanning from INT_MIN to INT_MAX is considered null. + constexpr Representation minInt((std::numeric_limits<int>::min)()); + constexpr Representation maxInt((std::numeric_limits<int>::max)()); + const auto adjustedX1 = Q_UNLIKELY(x1 == minInt) ? maxInt : x1 - 1; + const auto adjustedY1 = Q_UNLIKELY(y1 == minInt) ? maxInt : y1 - 1; + return x2 == adjustedX1 && y2 == adjustedY1; +} constexpr inline bool QRect::isEmpty() const noexcept { return x1 > x2 || y1 > y2; } @@ -171,52 +190,52 @@ constexpr inline bool QRect::isValid() const noexcept { return x1 <= x2 && y1 <= y2; } constexpr inline int QRect::left() const noexcept -{ return x1; } +{ return x1.value(); } constexpr inline int QRect::top() const noexcept -{ return y1; } +{ return y1.value(); } constexpr inline int QRect::right() const noexcept -{ return x2; } +{ return x2.value(); } constexpr inline int QRect::bottom() const noexcept -{ return y2; } +{ return y2.value(); } constexpr inline int QRect::x() const noexcept -{ return x1; } +{ return x1.value(); } constexpr inline int QRect::y() const noexcept -{ return y1; } +{ return y1.value(); } constexpr inline void QRect::setLeft(int pos) noexcept -{ x1 = pos; } +{ x1.setValue(pos); } constexpr inline void QRect::setTop(int pos) noexcept -{ y1 = pos; } +{ y1.setValue(pos); } constexpr inline void QRect::setRight(int pos) noexcept -{ x2 = pos; } +{ x2.setValue(pos); } constexpr inline void QRect::setBottom(int pos) noexcept -{ y2 = pos; } +{ y2.setValue(pos); } constexpr inline void QRect::setTopLeft(const QPoint &p) noexcept -{ x1 = p.x(); y1 = p.y(); } +{ x1.setValue(p.x()); y1.setValue(p.y()); } constexpr inline void QRect::setBottomRight(const QPoint &p) noexcept -{ x2 = p.x(); y2 = p.y(); } +{ x2.setValue(p.x()); y2.setValue(p.y()); } constexpr inline void QRect::setTopRight(const QPoint &p) noexcept -{ x2 = p.x(); y1 = p.y(); } +{ x2.setValue(p.x()); y1.setValue(p.y()); } constexpr inline void QRect::setBottomLeft(const QPoint &p) noexcept -{ x1 = p.x(); y2 = p.y(); } +{ x1.setValue(p.x()); y2.setValue(p.y()); } constexpr inline void QRect::setX(int ax) noexcept -{ x1 = ax; } +{ x1.setValue(ax); } constexpr inline void QRect::setY(int ay) noexcept -{ y1 = ay; } +{ y1.setValue(ay); } constexpr inline QPoint QRect::topLeft() const noexcept { return QPoint(x1, y1); } @@ -231,13 +250,17 @@ constexpr inline QPoint QRect::bottomLeft() const noexcept { return QPoint(x1, y2); } constexpr inline QPoint QRect::center() const noexcept -{ return QPoint(int((qint64(x1)+x2)/2), int((qint64(y1)+y2)/2)); } // cast avoids overflow on addition +{ + // cast avoids overflow on addition + return QPoint(int((qint64(x1.value()) + x2.value()) / 2), + int((qint64(y1.value()) + y2.value()) / 2)); +} constexpr inline int QRect::width() const noexcept -{ return x2 - x1 + 1; } +{ return (x2 - x1 + 1).value(); } constexpr inline int QRect::height() const noexcept -{ return y2 - y1 + 1; } +{ return (y2 - y1 + 1).value(); } constexpr inline QSize QRect::size() const noexcept { return QSize(width(), height()); } @@ -269,36 +292,38 @@ constexpr inline QRect QRect::transposed() const noexcept constexpr inline void QRect::moveTo(int ax, int ay) noexcept { - x2 += ax - x1; - y2 += ay - y1; - x1 = ax; - y1 = ay; + Representation rax(ax); + Representation ray(ay); + x2 += rax - x1; + y2 += ray - y1; + x1 = rax; + y1 = ray; } constexpr inline void QRect::moveTo(const QPoint &p) noexcept { - x2 += p.x() - x1; - y2 += p.y() - y1; - x1 = p.x(); - y1 = p.y(); + x2 += Representation(p.x()) - x1; + y2 += Representation(p.y()) - y1; + x1 = Representation(p.x()); + y1 = Representation(p.y()); } constexpr inline void QRect::moveLeft(int pos) noexcept -{ x2 += (pos - x1); x1 = pos; } +{ x2 += (pos - x1); x1.setValue(pos); } constexpr inline void QRect::moveTop(int pos) noexcept -{ y2 += (pos - y1); y1 = pos; } +{ y2 += (pos - y1); y1.setValue(pos); } constexpr inline void QRect::moveRight(int pos) noexcept { x1 += (pos - x2); - x2 = pos; + x2.setValue(pos); } constexpr inline void QRect::moveBottom(int pos) noexcept { y1 += (pos - y2); - y2 = pos; + y2.setValue(pos); } constexpr inline void QRect::moveTopLeft(const QPoint &p) noexcept @@ -327,8 +352,8 @@ constexpr inline void QRect::moveBottomLeft(const QPoint &p) noexcept constexpr inline void QRect::moveCenter(const QPoint &p) noexcept { - int w = x2 - x1; - int h = y2 - y1; + auto w = x2 - x1; + auto h = y2 - y1; x1 = p.x() - w/2; y1 = p.y() - h/2; x2 = x1 + w; @@ -337,34 +362,34 @@ constexpr inline void QRect::moveCenter(const QPoint &p) noexcept constexpr inline void QRect::getRect(int *ax, int *ay, int *aw, int *ah) const { - *ax = x1; - *ay = y1; - *aw = x2 - x1 + 1; - *ah = y2 - y1 + 1; + *ax = x1.value(); + *ay = y1.value(); + *aw = (x2 - x1 + 1).value(); + *ah = (y2 - y1 + 1).value(); } constexpr inline void QRect::setRect(int ax, int ay, int aw, int ah) noexcept { - x1 = ax; - y1 = ay; - x2 = (ax + aw - 1); - y2 = (ay + ah - 1); + x1.setValue(ax); + y1.setValue(ay); + x2 = (x1 + aw - 1); + y2 = (y1 + ah - 1); } constexpr inline void QRect::getCoords(int *xp1, int *yp1, int *xp2, int *yp2) const { - *xp1 = x1; - *yp1 = y1; - *xp2 = x2; - *yp2 = y2; + *xp1 = x1.value(); + *yp1 = y1.value(); + *xp2 = x2.value(); + *yp2 = y2.value(); } constexpr inline void QRect::setCoords(int xp1, int yp1, int xp2, int yp2) noexcept { - x1 = xp1; - y1 = yp1; - x2 = xp2; - y2 = yp2; + x1.setValue(xp1); + y1.setValue(yp1); + x2.setValue(xp2); + y2.setValue(yp2); } constexpr inline QRect QRect::adjusted(int xp1, int yp1, int xp2, int yp2) const noexcept @@ -648,7 +673,10 @@ constexpr inline QRectF::QRectF(const QPointF &atopLeft, const QPointF &abottomR } constexpr inline QRectF::QRectF(const QRect &r) noexcept - : xp(r.x()), yp(r.y()), w(r.width()), h(r.height()) + : xp(r.x()), + yp(r.y()), + w(qint64(r.right()) - r.left() + 1), + h(qint64(r.bottom()) - r.top() + 1) { } diff --git a/src/corelib/tools/qsize.cpp b/src/corelib/tools/qsize.cpp index 27ff1d164d1..08e89748ac7 100644 --- a/src/corelib/tools/qsize.cpp +++ b/src/corelib/tools/qsize.cpp @@ -192,19 +192,19 @@ QSize QSize::scaled(const QSize &s, Qt::AspectRatioMode mode) const noexcept return s; } else { bool useHeight; - qint64 rw = qint64(s.ht) * qint64(wd) / qint64(ht); + qint64 rw = qint64(s.height()) * qint64(width()) / qint64(height()); if (mode == Qt::KeepAspectRatio) { - useHeight = (rw <= s.wd); + useHeight = (rw <= s.width()); } else { // mode == Qt::KeepAspectRatioByExpanding - useHeight = (rw >= s.wd); + useHeight = (rw >= s.width()); } if (useHeight) { - return QSize(rw, s.ht); + return QSize(int(rw), s.height()); } else { - return QSize(s.wd, - qint32(qint64(s.wd) * qint64(ht) / qint64(wd))); + return QSize(s.width(), + qint32(qint64(s.width()) * qint64(height()) / qint64(width()))); } } } diff --git a/src/corelib/tools/qsize.h b/src/corelib/tools/qsize.h index 67f7146201c..75f1f8e70fb 100644 --- a/src/corelib/tools/qsize.h +++ b/src/corelib/tools/qsize.h @@ -4,6 +4,7 @@ #ifndef QSIZE_H #define QSIZE_H +#include <QtCore/qcheckedint_impl.h> #include <QtCore/qnamespace.h> #include <QtCore/qhashfunctions.h> #include <QtCore/qmargins.h> @@ -47,9 +48,9 @@ public: [[nodiscard]] constexpr inline QSize boundedTo(const QSize &) const noexcept; [[nodiscard]] constexpr QSize grownBy(QMargins m) const noexcept - { return {width() + m.left() + m.right(), height() + m.top() + m.bottom()}; } + { return {wd + m.left() + m.right(), ht + m.top() + m.bottom()}; } [[nodiscard]] constexpr QSize shrunkBy(QMargins m) const noexcept - { return {width() - m.left() - m.right(), height() - m.top() - m.bottom()}; } + { return {wd - m.left() - m.right(), ht - m.top() - m.bottom()}; } constexpr inline int &rwidth() noexcept; constexpr inline int &rheight() noexcept; @@ -68,11 +69,14 @@ private: friend inline constexpr QSize operator-(const QSize &s1, const QSize &s2) noexcept { return QSize(s1.wd - s2.wd, s1.ht - s2.ht); } friend inline constexpr QSize operator*(const QSize &s, qreal c) noexcept - { return QSize(qRound(s.wd * c), qRound(s.ht * c)); } + { return QSize(qRound(s.width() * c), qRound(s.height() * c)); } friend inline constexpr QSize operator*(qreal c, const QSize &s) noexcept { return s * c; } friend inline QSize operator/(const QSize &s, qreal c) - { Q_ASSERT(!qFuzzyIsNull(c)); return QSize(qRound(s.wd / c), qRound(s.ht / c)); } + { + Q_ASSERT(!qFuzzyIsNull(c)); + return QSize(qRound(s.width() / c), qRound(s.height() / c)); + } friend inline constexpr size_t qHash(const QSize &, size_t) noexcept; public: @@ -83,8 +87,14 @@ public: [[nodiscard]] inline constexpr QSizeF toSizeF() const noexcept; private: - int wd; - int ht; + using Representation = QtPrivate::QCheckedIntegers::QCheckedInt<int>; + + constexpr QSize(Representation w, Representation h) noexcept + : wd(w), ht(h) + {} + + Representation wd; + Representation ht; template <std::size_t I, typename S, @@ -93,9 +103,9 @@ private: friend constexpr decltype(auto) get(S &&s) noexcept { if constexpr (I == 0) - return q23::forward_like<S>(s.wd); + return q23::forward_like<S>(s.wd).as_underlying(); else if constexpr (I == 1) - return q23::forward_like<S>(s.ht); + return q23::forward_like<S>(s.ht).as_underlying(); } }; Q_DECLARE_TYPEINFO(QSize, Q_RELOCATABLE_TYPE); @@ -128,16 +138,16 @@ constexpr inline bool QSize::isValid() const noexcept { return wd >= 0 && ht >= 0; } constexpr inline int QSize::width() const noexcept -{ return wd; } +{ return wd.value(); } constexpr inline int QSize::height() const noexcept -{ return ht; } +{ return ht.value(); } constexpr inline void QSize::setWidth(int w) noexcept -{ wd = w; } +{ wd.setValue(w); } constexpr inline void QSize::setHeight(int h) noexcept -{ ht = h; } +{ ht.setValue(h); } constexpr inline QSize QSize::transposed() const noexcept { return QSize(ht, wd); } @@ -152,10 +162,10 @@ inline QSize QSize::scaled(int w, int h, Qt::AspectRatioMode mode) const noexcep { return scaled(QSize(w, h), mode); } constexpr inline int &QSize::rwidth() noexcept -{ return wd; } +{ return wd.as_underlying(); } constexpr inline int &QSize::rheight() noexcept -{ return ht; } +{ return ht.as_underlying(); } constexpr inline QSize &QSize::operator+=(const QSize &s) noexcept { @@ -173,19 +183,19 @@ constexpr inline QSize &QSize::operator-=(const QSize &s) noexcept constexpr inline QSize &QSize::operator*=(qreal c) noexcept { - wd = qRound(wd * c); - ht = qRound(ht * c); + wd.setValue(qRound(width() * c)); + ht.setValue(qRound(height() * c)); return *this; } constexpr inline size_t qHash(const QSize &s, size_t seed = 0) noexcept -{ return qHashMulti(seed, s.wd, s.ht); } +{ return qHashMulti(seed, s.width(), s.height()); } inline QSize &QSize::operator/=(qreal c) { Q_ASSERT(!qFuzzyIsNull(c)); - wd = qRound(wd / c); - ht = qRound(ht / c); + wd.setValue(qRound(width() / c)); + ht.setValue(qRound(height() / c)); return *this; } diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 768e92a15bf..1ea63f40965 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -1281,7 +1281,7 @@ QPolygonF QTransform::map(const QPolygonF &a) const QPointF *dp = p.data(); for(i = 0; i < size; ++i) { - do_map(da[i].xp, da[i].yp, dp[i].xp, dp[i].yp); + do_map(da[i].x(), da[i].y(), dp[i].rx(), dp[i].ry()); } return p; } @@ -1309,9 +1309,9 @@ QPolygon QTransform::map(const QPolygon &a) const for(i = 0; i < size; ++i) { qreal nx = 0, ny = 0; - do_map(da[i].xp, da[i].yp, nx, ny); - dp[i].xp = qRound(nx); - dp[i].yp = qRound(ny); + do_map(da[i].x(), da[i].y(), nx, ny); + dp[i].rx() = qRound(nx); + dp[i].ry() = qRound(ny); } return p; } diff --git a/tests/auto/corelib/tools/qline/tst_qline.cpp b/tests/auto/corelib/tools/qline/tst_qline.cpp index 8ff003ce986..08ec93cd20d 100644 --- a/tests/auto/corelib/tools/qline/tst_qline.cpp +++ b/tests/auto/corelib/tools/qline/tst_qline.cpp @@ -637,6 +637,19 @@ void tst_QLine::toLineF() QCOMPARE(input.toLineF(), result); } +namespace ConstexprTests { +constexpr QLine l = QLine(1, 2, 3, 4).translated(5, 6); +static_assert(l.x1() == 6); +static_assert(l.x2() == 8); +static_assert(l.y1() == 8); +static_assert(l.y2() == 10); + +constexpr QLineF lf = QLineF(1.0, 2.0, 3.0, 4.0).translated(5.0, 6.0); +static_assert(lf.x1() == 6.0); +static_assert(lf.x2() == 8.0); +static_assert(lf.y1() == 8.0); +static_assert(lf.y2() == 10.0); +} // namespace ConstexprTests QTEST_MAIN(tst_QLine) #include "tst_qline.moc" diff --git a/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp b/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp index dc0b0e4085f..a54534f9f87 100644 --- a/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp +++ b/tests/auto/corelib/tools/qmargins/tst_qmargins.cpp @@ -476,5 +476,19 @@ void tst_QMargins::toMarginsF() QCOMPARE(input.toMarginsF(), result); } +namespace ConstexprTests { +constexpr QMargins m = (QMargins(1, 2, 3, 4) + QMargins(5, 6, 7, 8)) * 2; +static_assert(m.left() == 12); +static_assert(m.top() == 16); +static_assert(m.right() == 20); +static_assert(m.bottom() == 24); + +constexpr QMarginsF mf = (QMarginsF(1.0, 2.0, 3.0, 4.0) + QMargins(5.0, 6.0, 7.0, 8.0)) * 2.5; +static_assert(mf.left() == 15.0); +static_assert(mf.top() == 20.0); +static_assert(mf.right() == 25.0); +static_assert(mf.bottom() == 30.0); +} // namespace ConstexprTests + QTEST_APPLESS_MAIN(tst_QMargins) #include "tst_qmargins.moc" diff --git a/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp b/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp index 4763c1bf07e..d80b2a7a189 100644 --- a/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp +++ b/tests/auto/corelib/tools/qpoint/tst_qpoint.cpp @@ -474,5 +474,11 @@ void tst_QPoint::structuredBinding() } } +namespace ConstexprTests { +constexpr QPoint p = (QPoint(1, 2) + QPoint(3, 4)) * 2; +static_assert(p.x() == 8); +static_assert(p.y() == 12); +} // namespace ConstexprTests + QTEST_MAIN(tst_QPoint) #include "tst_qpoint.moc" diff --git a/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp b/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp index ebbac4ec7c1..b4cb6c5d289 100644 --- a/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp +++ b/tests/auto/corelib/tools/qpointf/tst_qpointf.cpp @@ -552,5 +552,11 @@ void tst_QPointF::structuredBinding() } } +namespace ConstexprTests { +constexpr QPointF p = (QPointF(1.0, 2.0) + QPointF(3.0, 4.0)) * 2.5; +static_assert(p.x() == 10.0); +static_assert(p.y() == 15.0); +} // namespace ConstexprTests + QTEST_MAIN(tst_QPointF) #include "tst_qpointf.moc" diff --git a/tests/auto/corelib/tools/qrect/tst_qrect.cpp b/tests/auto/corelib/tools/qrect/tst_qrect.cpp index d7312ffc322..dd3963837d5 100644 --- a/tests/auto/corelib/tools/qrect/tst_qrect.cpp +++ b/tests/auto/corelib/tools/qrect/tst_qrect.cpp @@ -332,29 +332,31 @@ void tst_QRect::isNull_data() { QTest::addColumn<QRect>("r"); QTest::addColumn<bool>("isNull"); - - QTest::newRow( "InvalidQRect" ) << getQRectCase( InvalidQRect ) << true; - QTest::newRow( "SmallestQRect" ) << getQRectCase( SmallestQRect ) << false; - QTest::newRow( "MiddleQRect" ) << getQRectCase( MiddleQRect ) << false; - QTest::newRow( "LargestQRect" ) << getQRectCase( LargestQRect ) << false; - QTest::newRow( "SmallestCoordQRect" ) << getQRectCase( SmallestCoordQRect ) << false; - QTest::newRow( "LargestCoordQRect" ) << getQRectCase( LargestCoordQRect ) << true; // Due to overflow - QTest::newRow( "RandomQRect" ) << getQRectCase( RandomQRect ) << false; - QTest::newRow( "NegativeSizeQRect" ) << getQRectCase( NegativeSizeQRect ) << false; - QTest::newRow( "NegativePointQRect" ) << getQRectCase( NegativePointQRect ) << false; - QTest::newRow( "NullQRect" ) << getQRectCase( NullQRect ) << true; - QTest::newRow( "EmptyQRect" ) << getQRectCase( EmptyQRect ) << true; + QTest::addColumn<bool>("isNullF"); + + QTest::newRow( "InvalidQRect" ) << getQRectCase( InvalidQRect ) << true << true; + QTest::newRow( "SmallestQRect" ) << getQRectCase( SmallestQRect ) << false << false; + QTest::newRow( "MiddleQRect" ) << getQRectCase( MiddleQRect ) << false << false; + QTest::newRow( "LargestQRect" ) << getQRectCase( LargestQRect ) << false << false; + QTest::newRow( "SmallestCoordQRect" ) << getQRectCase( SmallestCoordQRect ) << false << false; + QTest::newRow( "LargestCoordQRect" ) << getQRectCase( LargestCoordQRect ) << true << false; // Due to overflow + QTest::newRow( "RandomQRect" ) << getQRectCase( RandomQRect ) << false << false; + QTest::newRow( "NegativeSizeQRect" ) << getQRectCase( NegativeSizeQRect ) << false << false; + QTest::newRow( "NegativePointQRect" ) << getQRectCase( NegativePointQRect ) << false << false; + QTest::newRow( "NullQRect" ) << getQRectCase( NullQRect ) << true << true; + QTest::newRow( "EmptyQRect" ) << getQRectCase( EmptyQRect ) << true << true; } void tst_QRect::isNull() { QFETCH( QRect, r ); QFETCH( bool, isNull ); + QFETCH( bool, isNullF ); QRectF rf(r); - QVERIFY( r.isNull() == isNull ); - QVERIFY( rf.isNull() == isNull ); + QCOMPARE( r.isNull(), isNull ); + QCOMPARE( rf.isNull(), isNullF ); } void tst_QRect::fuzzyIsNull() @@ -553,13 +555,7 @@ void tst_QRect::right() QFETCH( int, right ); QCOMPARE( r.right(), right ); - - if (isLarge(r.width())) - return; - // width overflow - if (r.left() < r.right() && r.width() < 0) - return; - QCOMPARE(QRectF(r).right(), qreal(right+1)); + QCOMPARE(QRectF(r).right(), qreal(right) + 1); } void tst_QRect::bottom_data() @@ -588,13 +584,7 @@ void tst_QRect::bottom() QFETCH( int, bottom ); QCOMPARE( r.bottom(), bottom ); - - if (isLarge(r.height())) - return; - // height overflow - if (r.top() < r.bottom() && r.height() < 0) - return; - QCOMPARE(QRectF(r).bottom(), qreal(bottom + 1)); + QCOMPARE(QRectF(r).bottom(), qreal(bottom) + 1); } void tst_QRect::x_data() @@ -2545,8 +2535,9 @@ void tst_QRect::newMoveLeft_data() } { - QTest::newRow( "SmallestQRect_MinimumInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MinimumInt ) - << QRect( QPoint(INT_MIN,1), QPoint(INT_MIN,1) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestQRect_MinimumInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MinimumInt ) + // << QRect( QPoint(INT_MIN,1), QPoint(INT_MIN,1) ); QTest::newRow( "SmallestQRect_MiddleNegativeInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MiddleNegativeInt ) << QRect( QPoint(INT_MIN/2,1), QPoint(INT_MIN/2,1) ); QTest::newRow( "SmallestQRect_ZeroInt" ) << getQRectCase( SmallestQRect ) << getIntCase( ZeroInt ) @@ -2560,12 +2551,15 @@ void tst_QRect::newMoveLeft_data() } { - QTest::newRow( "MiddleQRect_MinimumInt" ) << getQRectCase( MiddleQRect ) << getIntCase( MinimumInt ) - << QRect( QPoint(INT_MIN, INT_MIN / 2 ), QPoint( (INT_MAX/2)+(INT_MIN-INT_MIN/2), INT_MAX / 2 ) ); - QTest::newRow( "MiddleQRect_MiddleNegativeInt" ) << getQRectCase( MiddleQRect ) << getIntCase( MiddleNegativeInt ) - << QRect( QPoint(INT_MIN/2, INT_MIN / 2 ), QPoint((INT_MAX/2)+(INT_MIN/2-INT_MIN/2), INT_MAX / 2 ) ); - QTest::newRow( "MiddleQRect_ZeroInt" ) << getQRectCase( MiddleQRect ) << getIntCase( ZeroInt ) - << QRect( QPoint(0, INT_MIN / 2 ), QPoint((INT_MAX/2)+(0-INT_MIN/2),INT_MAX/2)); + // Not tested as it would cause an overflow + // QTest::newRow( "MiddleQRect_MinimumInt" ) << getQRectCase( MiddleQRect ) << getIntCase( MinimumInt ) + // << QRect( QPoint(INT_MIN, INT_MIN / 2 ), QPoint( (INT_MAX/2)+(INT_MIN-INT_MIN/2), INT_MAX / 2 ) ); + // Not tested as it would cause an overflow + // QTest::newRow( "MiddleQRect_MiddleNegativeInt" ) << getQRectCase( MiddleQRect ) << getIntCase( MiddleNegativeInt ) + // << QRect( QPoint(INT_MIN/2, INT_MIN / 2 ), QPoint((INT_MAX/2)+(INT_MIN/2-INT_MIN/2), INT_MAX / 2 ) ); + // Not tested as it would cause an overflow + // QTest::newRow( "MiddleQRect_ZeroInt" ) << getQRectCase( MiddleQRect ) << getIntCase( ZeroInt ) + // << QRect( QPoint(0, INT_MIN / 2 ), QPoint((INT_MAX/2)+(0-INT_MIN/2),INT_MAX/2)); // QTest::newRow( "MiddleQRect_MiddlePositiveInt" ) -- Not tested as it would cause an overflow // QTest::newRow( "MiddleQRect_MaximumInt" ) -- Not tested as it would cause an overflow // QTest::newRow( "MiddleQRect_RandomInt" ) -- Not tested as it would cause an overflow @@ -2587,14 +2581,18 @@ void tst_QRect::newMoveLeft_data() // QTest::newRow( "SmallestCoordQRect_MinimumInt" ) -- Not tested as it would cause an overflow QTest::newRow( "SmallestCoordQRect_MiddleNegativeInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( MiddleNegativeInt ) << QRect( QPoint( INT_MIN/2, INT_MIN ), QPoint(INT_MIN/2, INT_MIN ) ); - QTest::newRow( "SmallestCoordQRect_ZeroInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( ZeroInt ) - << QRect( QPoint( 0, INT_MIN ), QPoint(0, INT_MIN ) ); - QTest::newRow( "SmallestCoordQRect_MiddlePositiveInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( MiddlePositiveInt ) - << QRect( QPoint( INT_MAX/2, INT_MIN ), QPoint(INT_MAX/2, INT_MIN ) ); - QTest::newRow( "SmallestCoordQRect_MaximumInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( MaximumInt ) - << QRect( QPoint( INT_MAX, INT_MIN ), QPoint(INT_MAX, INT_MIN ) ); - QTest::newRow( "SmallestCoordQRect_RandomInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( RandomInt ) - << QRect( QPoint( 4953, INT_MIN ), QPoint(4953, INT_MIN ) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestCoordQRect_ZeroInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( ZeroInt ) + // << QRect( QPoint( 0, INT_MIN ), QPoint(0, INT_MIN ) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestCoordQRect_MiddlePositiveInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( MiddlePositiveInt ) + // << QRect( QPoint( INT_MAX/2, INT_MIN ), QPoint(INT_MAX/2, INT_MIN ) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestCoordQRect_MaximumInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( MaximumInt ) + // << QRect( QPoint( INT_MAX, INT_MIN ), QPoint(INT_MAX, INT_MIN ) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestCoordQRect_RandomInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( RandomInt ) + // << QRect( QPoint( 4953, INT_MIN ), QPoint(4953, INT_MIN ) ); } { @@ -2607,8 +2605,9 @@ void tst_QRect::newMoveLeft_data() } { - QTest::newRow( "RandomQRect_MinimumInt" ) << getQRectCase( RandomQRect ) << getIntCase( MinimumInt ) - << QRect( QPoint( INT_MIN, 200 ), QPoint(10+INT_MIN, 215 ) ); + // Not tested as it would cause an overflow + // QTest::newRow( "RandomQRect_MinimumInt" ) << getQRectCase( RandomQRect ) << getIntCase( MinimumInt ) + // << QRect( QPoint( INT_MIN, 200 ), QPoint(10+INT_MIN, 215 ) ); QTest::newRow( "RandomQRect_MiddleNegativeInt" ) << getQRectCase( RandomQRect ) << getIntCase( MiddleNegativeInt ) << QRect( QPoint( INT_MIN/2, 200 ), QPoint(10+INT_MIN/2, 215 ) ); QTest::newRow( "RandomQRect_ZeroInt" ) << getQRectCase( RandomQRect ) << getIntCase( ZeroInt ) @@ -2709,8 +2708,9 @@ void tst_QRect::newMoveTop_data() } { - QTest::newRow( "SmallestQRect_MinimumInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MinimumInt ) - << QRect( QPoint(1,INT_MIN), QPoint(1,INT_MIN) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestQRect_MinimumInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MinimumInt ) + // << QRect( QPoint(1,INT_MIN), QPoint(1,INT_MIN) ); QTest::newRow( "SmallestQRect_MiddleNegativeInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MiddleNegativeInt ) << QRect( QPoint(1,INT_MIN/2), QPoint(1,INT_MIN/2) ); QTest::newRow( "SmallestQRect_ZeroInt" ) << getQRectCase( SmallestQRect ) << getIntCase( ZeroInt ) @@ -2751,14 +2751,18 @@ void tst_QRect::newMoveTop_data() // QTest::newRow( "SmallestCoordQRect_MinimumInt" ) -- Not tested as it would cause an overflow QTest::newRow( "SmallestCoordQRect_MiddleNegativeInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( MiddleNegativeInt ) << QRect( QPoint(INT_MIN,INT_MIN/2), QPoint(INT_MIN,INT_MIN/2) ); - QTest::newRow( "SmallestCoordQRect_ZeroInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( ZeroInt ) - << QRect( QPoint(INT_MIN,0), QPoint(INT_MIN,0) ); - QTest::newRow( "SmallestCoordQRect_MiddlePositiveInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( MiddlePositiveInt ) - << QRect( QPoint(INT_MIN,INT_MAX/2), QPoint(INT_MIN,INT_MAX/2) ); - QTest::newRow( "SmallestCoordQRect_MaximumInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( MaximumInt ) - << QRect( QPoint(INT_MIN,INT_MAX), QPoint(INT_MIN,INT_MAX) ); - QTest::newRow( "SmallestCoordQRect_RandomInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( RandomInt ) - << QRect( QPoint(INT_MIN,4953), QPoint(INT_MIN,4953) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestCoordQRect_ZeroInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( ZeroInt ) + // << QRect( QPoint(INT_MIN,0), QPoint(INT_MIN,0) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestCoordQRect_MiddlePositiveInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( MiddlePositiveInt ) + // << QRect( QPoint(INT_MIN,INT_MAX/2), QPoint(INT_MIN,INT_MAX/2) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestCoordQRect_MaximumInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( MaximumInt ) + // << QRect( QPoint(INT_MIN,INT_MAX), QPoint(INT_MIN,INT_MAX) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestCoordQRect_RandomInt" ) << getQRectCase( SmallestCoordQRect ) << getIntCase( RandomInt ) + // << QRect( QPoint(INT_MIN,4953), QPoint(INT_MIN,4953) ); } { @@ -2771,8 +2775,9 @@ void tst_QRect::newMoveTop_data() } { - QTest::newRow( "RandomQRect_MinimumInt" ) << getQRectCase( RandomQRect ) << getIntCase( MinimumInt ) - << QRect( QPoint(100,INT_MIN), QPoint(110,15+INT_MIN) ); + // Not tested as it would cause an overflow + // QTest::newRow( "RandomQRect_MinimumInt" ) << getQRectCase( RandomQRect ) << getIntCase( MinimumInt ) + // << QRect( QPoint(100,INT_MIN), QPoint(110,15+INT_MIN) ); QTest::newRow( "RandomQRect_MiddleNegativeInt" ) << getQRectCase( RandomQRect ) << getIntCase( MiddleNegativeInt ) << QRect( QPoint(100,INT_MIN/2), QPoint(110,15+INT_MIN/2) ); QTest::newRow( "RandomQRect_ZeroInt" ) << getQRectCase( RandomQRect ) << getIntCase( ZeroInt ) @@ -2872,8 +2877,9 @@ void tst_QRect::newMoveRight_data() } { - QTest::newRow( "SmallestQRect_MinimumInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MinimumInt ) - << QRect( QPoint(INT_MIN,1), QPoint(INT_MIN,1) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestQRect_MinimumInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MinimumInt ) + // << QRect( QPoint(INT_MIN,1), QPoint(INT_MIN,1) ); QTest::newRow( "SmallestQRect_MiddleNegativeInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MiddleNegativeInt ) << QRect( QPoint(INT_MIN/2,1), QPoint(INT_MIN/2,1) ); QTest::newRow( "SmallestQRect_ZeroInt" ) << getQRectCase( SmallestQRect ) << getIntCase( ZeroInt ) @@ -2981,8 +2987,9 @@ void tst_QRect::newMoveRight_data() } { - QTest::newRow( "EmptyQRect_MinimumInt" ) << getQRectCase( EmptyQRect ) << getIntCase( MinimumInt ) - << QRect( QPoint(INT_MIN+1,2 ), QPoint(INT_MIN, 1 ) ); + // Not tested as it would cause an overflow + // QTest::newRow( "EmptyQRect_MinimumInt" ) << getQRectCase( EmptyQRect ) << getIntCase( MinimumInt ) + // << QRect( QPoint(INT_MIN+1,2 ), QPoint(INT_MIN, 1 ) ); QTest::newRow( "EmptyQRect_MiddleNegativeInt" ) << getQRectCase( EmptyQRect ) << getIntCase( MiddleNegativeInt ) << QRect( QPoint(INT_MIN/2+1, 2 ), QPoint(INT_MIN/2, 1 ) ); QTest::newRow( "EmptyQRect_ZeroInt" ) << getQRectCase( EmptyQRect ) << getIntCase( ZeroInt ) @@ -3026,8 +3033,9 @@ void tst_QRect::newMoveBottom_data() } { - QTest::newRow( "SmallestQRect_MinimumInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MinimumInt ) - << QRect( QPoint(1,INT_MIN), QPoint(1,INT_MIN) ); + // Not tested as it would cause an overflow + // QTest::newRow( "SmallestQRect_MinimumInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MinimumInt ) + // << QRect( QPoint(1,INT_MIN), QPoint(1,INT_MIN) ); QTest::newRow( "SmallestQRect_MiddleNegativeInt" ) << getQRectCase( SmallestQRect ) << getIntCase( MiddleNegativeInt ) << QRect( QPoint(1,INT_MIN/2), QPoint(1,INT_MIN/2) ); QTest::newRow( "SmallestQRect_ZeroInt" ) << getQRectCase( SmallestQRect ) << getIntCase( ZeroInt ) @@ -3135,8 +3143,9 @@ void tst_QRect::newMoveBottom_data() } { - QTest::newRow( "EmptyQRect_MinimumInt" ) << getQRectCase( EmptyQRect ) << getIntCase( MinimumInt ) - << QRect( QPoint(2,INT_MIN+1), QPoint(1,INT_MIN) ); + // Not tested as it would cause an overflow + // QTest::newRow( "EmptyQRect_MinimumInt" ) << getQRectCase( EmptyQRect ) << getIntCase( MinimumInt ) + // << QRect( QPoint(2,INT_MIN+1), QPoint(1,INT_MIN) ); QTest::newRow( "EmptyQRect_MiddleNegativeInt" ) << getQRectCase( EmptyQRect ) << getIntCase( MiddleNegativeInt ) << QRect( QPoint(2,INT_MIN/2+1), QPoint(1,INT_MIN/2) ); QTest::newRow( "EmptyQRect_ZeroInt" ) << getQRectCase( EmptyQRect ) << getIntCase( ZeroInt ) @@ -3187,8 +3196,9 @@ void tst_QRect::newMoveTopLeft_data() { QTest::newRow("SmallestQRect_NullQPoint") << getQRectCase(SmallestQRect) << getQPointCase(NullQPoint) << QRect(QPoint(0,0), QPoint(0,0)); - QTest::newRow("SmallestQRect_SmallestCoordQPoint") << getQRectCase(SmallestQRect) << getQPointCase(SmallestCoordQPoint) - << QRect(QPoint(INT_MIN,INT_MIN), QPoint(INT_MIN,INT_MIN)); + // Not tested as it would cause an overflow + // QTest::newRow("SmallestQRect_SmallestCoordQPoint") << getQRectCase(SmallestQRect) << getQPointCase(SmallestCoordQPoint) + // << QRect(QPoint(INT_MIN,INT_MIN), QPoint(INT_MIN,INT_MIN)); QTest::newRow("SmallestQRect_MiddleNegCoordQPoint") << getQRectCase(SmallestQRect) << getQPointCase(MiddleNegCoordQPoint) << QRect(QPoint(INT_MIN/2,INT_MIN/2), QPoint(INT_MIN/2,INT_MIN/2)); QTest::newRow("SmallestQRect_MiddlePosCoordQPoint") << getQRectCase(SmallestQRect) << getQPointCase(MiddlePosCoordQPoint) @@ -3678,10 +3688,12 @@ void tst_QRect::transposed_data() QTest::newRow("InvalidQRect") << getQRectCase(InvalidQRect); QTest::newRow("SmallestQRect") << getQRectCase(SmallestQRect); - QTest::newRow("MiddleQRect") << getQRectCase(MiddleQRect); + // Not tested as it would cause an overflow + // QTest::newRow("MiddleQRect") << getQRectCase(MiddleQRect); QTest::newRow("LargestQRect") << getQRectCase(LargestQRect); QTest::newRow("SmallestCoordQRect") << getQRectCase(SmallestCoordQRect); - QTest::newRow("LargestCoordQRect") << getQRectCase(LargestCoordQRect); + // Not tested as it would cause an overflow + // QTest::newRow("LargestCoordQRect") << getQRectCase(LargestCoordQRect); QTest::newRow("RandomQRect") << getQRectCase(RandomQRect); QTest::newRow("NegativeSizeQRect") << getQRectCase(NegativeSizeQRect); QTest::newRow("NegativePointQRect") << getQRectCase(NegativePointQRect); @@ -4529,5 +4541,23 @@ void tst_QRect::debug() QCOMPARE(str, "QRect(-2147483648,-2147483648 4294967296x4294967296 (oversized))"); } +namespace ConstexprTests { +constexpr QRect r = QRect(1, 2, 3, 4).translated(10, 20); +static_assert(r.width() == 3); +static_assert(r.height() == 4); +static_assert(r.left() == 11); +static_assert(r.top() == 22); +static_assert(r.right() == 13); +static_assert(r.bottom() == 25); + +constexpr QRectF rf = QRectF(1.0, 2.0, 3.0, 4.0).translated(10.5, 20.5); +static_assert(rf.width() == 3.0); +static_assert(rf.height() == 4.0); +static_assert(rf.left() == 11.5); +static_assert(rf.top() == 22.5); +static_assert(rf.right() == 14.5); +static_assert(rf.bottom() == 26.5); +} // namespace ConstexprTests + QTEST_MAIN(tst_QRect) #include "tst_qrect.moc" diff --git a/tests/auto/corelib/tools/qsize/tst_qsize.cpp b/tests/auto/corelib/tools/qsize/tst_qsize.cpp index d379275dd8b..d5242b8be43 100644 --- a/tests/auto/corelib/tools/qsize/tst_qsize.cpp +++ b/tests/auto/corelib/tools/qsize/tst_qsize.cpp @@ -336,5 +336,11 @@ void tst_QSize::structuredBinding() } } +namespace ConstexprTests { +constexpr QSize s = (QSize(10, 20) + QSize(30, 40)) * 2; +static_assert(s.width() == 80); +static_assert(s.height() == 120); +} // namespace ConstexprTests + QTEST_APPLESS_MAIN(tst_QSize) #include "tst_qsize.moc" diff --git a/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp b/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp index bb087e89de9..2b26797baf9 100644 --- a/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp +++ b/tests/auto/corelib/tools/qsizef/tst_qsizef.cpp @@ -330,5 +330,11 @@ void tst_QSizeF::structuredBinding() } } +namespace ConstexprTests { +constexpr QSizeF s = (QSize(10.0, 20.0) + QSize(30.0, 40.0)) * 2.5; +static_assert(s.width() == 100.0); +static_assert(s.height() == 150.0); +} // namespace ConstexprTests + QTEST_APPLESS_MAIN(tst_QSizeF) #include "tst_qsizef.moc" |