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 <thiago.macieira@intel.com>
This commit is contained in:
Giuseppe D'Angelo 2025-02-02 22:17:42 +01:00
parent 56de113975
commit 1145e1709d
15 changed files with 405 additions and 249 deletions

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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)
{
}

View File

@ -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())));
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -332,29 +332,31 @@ void tst_QRect::isNull_data()
{
QTest::addColumn<QRect>("r");
QTest::addColumn<bool>("isNull");
QTest::addColumn<bool>("isNullF");
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::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"

View File

@ -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"

View File

@ -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"